mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-3924 Added askValues() method to GhidraScript. Allows users to enter
multiple values with one dialog.
This commit is contained in:
parent
87bd074603
commit
e71d5f8faf
45 changed files with 4856 additions and 235 deletions
|
@ -0,0 +1,92 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Example script for showing how to use the "AskValues" script method for inputing multiple values
|
||||||
|
// @category Examples
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.features.base.values.GhidraValuesMap;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.MessageType;
|
||||||
|
|
||||||
|
public class AskValuesExampleScript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
|
||||||
|
GhidraValuesMap values = new GhidraValuesMap();
|
||||||
|
|
||||||
|
values.defineString("Name");
|
||||||
|
values.defineAddress("Address", currentProgram);
|
||||||
|
values.defineInt("Count");
|
||||||
|
values.defineInt("Max Results", 100);
|
||||||
|
values.defineChoice("Priority", "Low", "Low", "Medium", "High");
|
||||||
|
|
||||||
|
// When asking for a program, you must supply a consumer that you will use
|
||||||
|
// to release the program. Since programs share open instances, Ghidra uses
|
||||||
|
// consumers to keep track of these uses. Scripts can just add themselves
|
||||||
|
// as the consumer (The askProgram() method does this for you). It is
|
||||||
|
// important to release it when you are done with. Optionally, you can also
|
||||||
|
// provide a tool in which case the program will also be opened in the tool (and the
|
||||||
|
// tool would then also add itself as a consumer). Otherwise, the program will not
|
||||||
|
// show up in the tool and when you release the consumer, it will be closed.
|
||||||
|
values.defineProgram("Other Program", this, state.getTool());
|
||||||
|
|
||||||
|
// Optional validator that can be set to validate values before the dialog returns. It
|
||||||
|
// is called when the "Ok" button is pushed and must return true before the dialog exits.
|
||||||
|
// It also includes a statusListener where messages can be set on the dialog. In this
|
||||||
|
// example, we are requiring that the name and program fields be populated
|
||||||
|
|
||||||
|
values.setValidator((valueMap, status) -> {
|
||||||
|
if (!valueMap.hasValue("Name")) {
|
||||||
|
status.setStatusText("Name must be filled in!", MessageType.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!valueMap.hasValue("Other Program")) {
|
||||||
|
status.setStatusText("Other Program must be filled it!", MessageType.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// asks the script to show a dialog where the user can give values for all the items
|
||||||
|
// in the ValuesMap.
|
||||||
|
|
||||||
|
values = askValues("Enter Example Script Values", null, values);
|
||||||
|
|
||||||
|
// if the user cancels the ask dialog, the script will exit as cancelled. Otherwise
|
||||||
|
// the returned ValuesMap will contain the results of the user filling in values from the
|
||||||
|
//dialog. The values map returned may or may not be the same instance as the one passed in.
|
||||||
|
|
||||||
|
String name = values.getString("Name");
|
||||||
|
Address address = values.getAddress("Address");
|
||||||
|
int age = values.getInt("Count");
|
||||||
|
int max = values.getInt("Max Results");
|
||||||
|
String priority = values.getChoice("Priority");
|
||||||
|
Program program = values.getProgram("Other Program");
|
||||||
|
|
||||||
|
println("Name = " + name);
|
||||||
|
println("Address = " + address);
|
||||||
|
println("Count = " + age);
|
||||||
|
println("Max Results = " + max);
|
||||||
|
println("Priority = " + priority);
|
||||||
|
println("Program = " + program);
|
||||||
|
|
||||||
|
// VERY IMPORTANT!!! you must release any programs when you are done with them!
|
||||||
|
// If you also opened in the tool, you can immediately release it because the tool will
|
||||||
|
// then keep it open.
|
||||||
|
program.release(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,8 +33,7 @@ public class ReloadSleighLanguage extends GhidraScript {
|
||||||
language.reloadLanguage(monitor);
|
language.reloadLanguage(monitor);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
Msg.showError(this, this.state.getParamPanel(), "Reload Sleigh Language Failed",
|
Msg.showError(this, null, "Reload Sleigh Language Failed", e.getMessage());
|
||||||
e.getMessage());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentProgram.setLanguage(language, null, true, monitor);
|
currentProgram.setLanguage(language, null, true, monitor);
|
||||||
|
|
|
@ -1,189 +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.script;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.GridLayout;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
|
|
||||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
|
||||||
import docking.widgets.filechooser.GhidraFileChooserPanel;
|
|
||||||
import docking.widgets.label.GLabel;
|
|
||||||
import ghidra.app.util.AddressInput;
|
|
||||||
|
|
||||||
public class GatherParamPanel extends JPanel {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public static final int STRING = 0;
|
|
||||||
public static final int FILE = 1;
|
|
||||||
public static final int DIRECTORY = 2;
|
|
||||||
public static final int ADDRESS = 3;
|
|
||||||
public static final int INTEGER = 4;
|
|
||||||
public static final int LANGUAGE = 5;
|
|
||||||
|
|
||||||
private GhidraState state;
|
|
||||||
private HashMap<String, ParamComponent> parameters;
|
|
||||||
private boolean shown;
|
|
||||||
|
|
||||||
public GatherParamPanel(GhidraState state) {
|
|
||||||
this.state = state;
|
|
||||||
setLayout(new GridLayout(0, 2));
|
|
||||||
parameters = new HashMap<>();
|
|
||||||
shown = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParamComponent getParameter(String key) {
|
|
||||||
return parameters.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearParameters() {
|
|
||||||
parameters.clear();
|
|
||||||
removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addParameterRegardless(String key, String label, int type, Object defaultValue) {
|
|
||||||
Component displayComponent = null;
|
|
||||||
if (type == FILE || type == DIRECTORY) {
|
|
||||||
String titleString = null;
|
|
||||||
if (type == DIRECTORY) {
|
|
||||||
titleString = "SELECT DIRECTORY";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
titleString = "SELECT FILE";
|
|
||||||
}
|
|
||||||
GhidraFileChooserPanel panel = new GhidraFileChooserPanel(titleString,
|
|
||||||
"Recipe.fileChooser", "", true, GhidraFileChooserPanel.INPUT_MODE);
|
|
||||||
if (type == DIRECTORY) {
|
|
||||||
panel.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
|
|
||||||
}
|
|
||||||
panel.setFileName(defaultValue.toString());
|
|
||||||
parameters.put(key, new ParamComponent(panel, type));
|
|
||||||
displayComponent = panel;
|
|
||||||
}
|
|
||||||
else if (type == ADDRESS) {
|
|
||||||
AddressInput addressInput = new AddressInput();
|
|
||||||
if (state.getCurrentProgram() != null) {
|
|
||||||
addressInput.setAddressFactory(state.getCurrentProgram().getAddressFactory());
|
|
||||||
}
|
|
||||||
addressInput.selectDefaultAddressSpace();
|
|
||||||
addressInput.select();
|
|
||||||
if (defaultValue != null) {
|
|
||||||
addressInput.setValue(defaultValue.toString());
|
|
||||||
}
|
|
||||||
displayComponent = addressInput;
|
|
||||||
parameters.put(key, new ParamComponent(displayComponent, type));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JTextField textField = new JTextField();
|
|
||||||
if (defaultValue != null) {
|
|
||||||
textField.setText(defaultValue.toString());
|
|
||||||
}
|
|
||||||
displayComponent = textField;
|
|
||||||
parameters.put(key, new ParamComponent(displayComponent, type));
|
|
||||||
}
|
|
||||||
add(new GLabel(label));
|
|
||||||
add(displayComponent);
|
|
||||||
shown = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addParameter(String key, String label, int type, Object defaultValue) {
|
|
||||||
if (parameters.containsKey(key) || state.getEnvironmentVar(key) != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addParameterRegardless(key, label, type, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParamsInState() {
|
|
||||||
for (String string2 : parameters.keySet()) {
|
|
||||||
String key = string2.toString();
|
|
||||||
ParamComponent pc = parameters.get(key);
|
|
||||||
switch (pc.getType()) {
|
|
||||||
case ADDRESS:
|
|
||||||
if (state.getCurrentProgram() != null) {
|
|
||||||
AddressInput addressInput = (AddressInput) pc.getDisplayComponent();
|
|
||||||
state.addEnvironmentVar(key, addressInput.getAddress());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AddressInput addressInput = (AddressInput) pc.getDisplayComponent();
|
|
||||||
state.addEnvironmentVar(key, addressInput.getValue().toString());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FILE:
|
|
||||||
case DIRECTORY:
|
|
||||||
GhidraFileChooserPanel gfcp = (GhidraFileChooserPanel) pc.getDisplayComponent();
|
|
||||||
state.addEnvironmentVar(key, new File(gfcp.getFileName()));
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
JTextField iTextField = (JTextField) pc.getDisplayComponent();
|
|
||||||
int val = Integer.parseInt(iTextField.getText());
|
|
||||||
state.addEnvironmentVar(key, val);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
JTextField textField = (JTextField) pc.getDisplayComponent();
|
|
||||||
state.addEnvironmentVar(key, textField.getText());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void currentProgramChanged() {
|
|
||||||
for (String string2 : parameters.keySet()) {//OMG!!
|
|
||||||
String key = string2.toString();
|
|
||||||
ParamComponent pc = parameters.get(key);
|
|
||||||
switch (pc.getType()) {
|
|
||||||
case ADDRESS:
|
|
||||||
AddressInput addressInput = (AddressInput) pc.getDisplayComponent();
|
|
||||||
addressInput.setAddressFactory(state.getCurrentProgram().getAddressFactory());
|
|
||||||
addressInput.selectDefaultAddressSpace();
|
|
||||||
addressInput.select();
|
|
||||||
if (panelShown()) {
|
|
||||||
state.addEnvironmentVar(key, addressInput.getAddress());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean panelShown() {
|
|
||||||
return shown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShown(boolean shown) {
|
|
||||||
this.shown = shown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ParamComponent {
|
|
||||||
private int type;
|
|
||||||
private Component displayComponent;
|
|
||||||
|
|
||||||
public ParamComponent(Component displayComponent, int type) {
|
|
||||||
this.displayComponent = displayComponent;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Component getDisplayComponent() {
|
|
||||||
return displayComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,12 +20,15 @@ import java.io.*;
|
||||||
import java.rmi.ConnectException;
|
import java.rmi.ConnectException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import docking.DockingWindowManager;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.PasswordDialog;
|
import docking.widgets.PasswordDialog;
|
||||||
import docking.widgets.dialogs.MultiLineMessageDialog;
|
import docking.widgets.dialogs.MultiLineMessageDialog;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
|
import docking.widgets.values.*;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
||||||
|
@ -44,6 +47,7 @@ import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.query.TableService;
|
import ghidra.app.util.query.TableService;
|
||||||
import ghidra.app.util.viewer.field.BrowserCodeUnitFormat;
|
import ghidra.app.util.viewer.field.BrowserCodeUnitFormat;
|
||||||
import ghidra.app.util.viewer.field.CommentUtils;
|
import ghidra.app.util.viewer.field.CommentUtils;
|
||||||
|
import ghidra.features.base.values.GhidraValuesMap;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.client.*;
|
import ghidra.framework.client.*;
|
||||||
import ghidra.framework.cmd.BackgroundCommand;
|
import ghidra.framework.cmd.BackgroundCommand;
|
||||||
|
@ -2327,6 +2331,66 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts for multiple values at the same time. To use this method, you must first
|
||||||
|
* create a {@link GhidraValuesMap} and define the values that will be supplied by this method.
|
||||||
|
* In the GUI environment, this will result in a single dialog with an entry for each value
|
||||||
|
* defined in the values map. This method returns a GhidraValuesMap with the values supplied by
|
||||||
|
* the user in GUI mode or command line arguments in headless mode. If the user cancels the
|
||||||
|
* dialog, a cancelled exception will be thrown, and unless it is explicity caught by the
|
||||||
|
* script, will terminate the script. Also, if the values map has a {@link ValuesMapValidator},
|
||||||
|
* the values will be validated when the user presses the "OK" button and will only exit the
|
||||||
|
* dialog if the validate check passes. Otherwise, the validator should have reported an error
|
||||||
|
* message in the dialog and the dialog will remain visible.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Regardless of environment -- if script arguments have been set, this method will use the
|
||||||
|
* next arguments in the array and advance the array index until all values in the values map
|
||||||
|
* have been satisfied and so the next call to an ask method will get the next argument after
|
||||||
|
* those consumed by this call.
|
||||||
|
*
|
||||||
|
* @param title the title of the dialog if in GUI mode
|
||||||
|
* @param optionalMessage an optional message that is displayed in the dialog, just above the
|
||||||
|
* list of name/value pairs
|
||||||
|
* @param values the GhidraValuesMap containing the values to include in the dialog.
|
||||||
|
* @return the GhidraValuesMap with values set from user input in the dialog (This is the same
|
||||||
|
* instance that was passed in, so you don't need to use this)
|
||||||
|
* @throws CancelledException if the user hit the 'cancel' button in GUI mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
public GhidraValuesMap askValues(String title, String optionalMessage, GhidraValuesMap values)
|
||||||
|
throws CancelledException {
|
||||||
|
for (AbstractValue<?> value : values.getValues()) {
|
||||||
|
String key = join(title, value.getName());
|
||||||
|
loadAskValue(value.getValue(), s -> value.setAsText(s), key);
|
||||||
|
}
|
||||||
|
if (isRunningHeadless()) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
String key = generateKey(values);
|
||||||
|
return doAsk(GValuesMap.class, title, key, values, v -> {
|
||||||
|
if (v != values) {
|
||||||
|
values.copyValues(v);
|
||||||
|
}
|
||||||
|
ValuesMapDialog dialog = new ValuesMapDialog(title, optionalMessage, values);
|
||||||
|
DockingWindowManager.showDialog(dialog);
|
||||||
|
if (dialog.isCancelled()) {
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
return (GhidraValuesMap) dialog.getValues();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string key unique to the values defined in the given ValuesMap. Used to store
|
||||||
|
* and load previously chosen values for the given values map.
|
||||||
|
* @param valuesMap the ValuesMap to generate a key for
|
||||||
|
* @return a string that is unique for the values defined in the given ValuesMap
|
||||||
|
*/
|
||||||
|
private String generateKey(GValuesMap valuesMap) {
|
||||||
|
return valuesMap.getValues().stream().map(v -> v.getName()).collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an int, using the String parameters for guidance. The actual behavior of the
|
* Returns an int, using the String parameters for guidance. The actual behavior of the
|
||||||
* method depends on your environment, which can be GUI or headless.
|
* method depends on your environment, which can be GUI or headless.
|
||||||
|
|
|
@ -18,8 +18,6 @@ package ghidra.app.script;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
|
|
||||||
import ghidra.app.events.*;
|
import ghidra.app.events.*;
|
||||||
import ghidra.framework.model.Project;
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.framework.plugintool.PluginEvent;
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
|
@ -29,7 +27,6 @@ import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +39,6 @@ public class GhidraState {
|
||||||
private ProgramSelection currentSelection;
|
private ProgramSelection currentSelection;
|
||||||
private ProgramSelection currentHighlight;
|
private ProgramSelection currentHighlight;
|
||||||
private HashMap<String, Object> envmap = new HashMap<>();
|
private HashMap<String, Object> envmap = new HashMap<>();
|
||||||
private GatherParamPanel gatherParamPanel;
|
|
||||||
private Project project;
|
private Project project;
|
||||||
private final boolean isGlobalState;
|
private final boolean isGlobalState;
|
||||||
|
|
||||||
|
@ -64,11 +60,6 @@ public class GhidraState {
|
||||||
this.currentSelection = selection;
|
this.currentSelection = selection;
|
||||||
this.currentHighlight = highlight;
|
this.currentHighlight = highlight;
|
||||||
this.isGlobalState = true;
|
this.isGlobalState = true;
|
||||||
if (!SystemUtilities.isInHeadlessMode()) {
|
|
||||||
Swing.runNow(() -> {
|
|
||||||
gatherParamPanel = new GatherParamPanel(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GhidraState(GhidraState state) {
|
public GhidraState(GhidraState state) {
|
||||||
|
@ -115,10 +106,6 @@ public class GhidraState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.currentProgram = program;
|
this.currentProgram = program;
|
||||||
if (gatherParamPanel == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gatherParamPanel.currentProgramChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -252,34 +239,6 @@ public class GhidraState {
|
||||||
return envmap.get(name);
|
return envmap.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addParameter(String key, String label, int type, Object defaultValue) {
|
|
||||||
if (gatherParamPanel == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gatherParamPanel.addParameter(key, label, type, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean displayParameterGatherer(String title) {
|
|
||||||
if (gatherParamPanel == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!gatherParamPanel.panelShown()) {
|
|
||||||
int ans = JOptionPane.showConfirmDialog(null, gatherParamPanel, title,
|
|
||||||
JOptionPane.OK_CANCEL_OPTION);
|
|
||||||
if (ans == JOptionPane.CANCEL_OPTION) {
|
|
||||||
gatherParamPanel.setShown(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
gatherParamPanel.setShown(true);
|
|
||||||
gatherParamPanel.setParamsInState();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GatherParamPanel getParamPanel() {
|
|
||||||
return gatherParamPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getEnvironmentNames() {
|
public Set<String> getEnvironmentNames() {
|
||||||
return envmap.keySet();
|
return envmap.keySet();
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class SelectLanguageDialog extends DialogComponentProvider {
|
||||||
wasCancelled = true;
|
wasCancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean wasCancelled() {
|
public boolean wasCancelled() {
|
||||||
return wasCancelled;
|
return wasCancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ public class SelectLanguageDialog extends DialogComponentProvider {
|
||||||
return languagePanel.getSelectedLcsPair() != null;
|
return languagePanel.getSelectedLcsPair() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSelectedLanguage(LanguageCompilerSpecPair language) {
|
public void setSelectedLanguage(LanguageCompilerSpecPair language) {
|
||||||
Swing.runNow(() -> languagePanel.setSelectedLcsPair(language));
|
Swing.runNow(() -> languagePanel.setSelectedLcsPair(language));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||||
/**
|
/**
|
||||||
* Returns the address in the field or null if the address can't
|
* Returns the address in the field or null if the address can't
|
||||||
* be parsed.
|
* be parsed.
|
||||||
|
* @return The address for the current value in the text field
|
||||||
*
|
*
|
||||||
* @throws NullPointerException if AddressFactory has not been set.
|
* @throws NullPointerException if AddressFactory has not been set.
|
||||||
*/
|
*/
|
||||||
|
@ -160,6 +161,14 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||||
return textField.getText().length() != 0;
|
return textField.getText().length() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text in this field.
|
||||||
|
* @return the text in this field
|
||||||
|
*/
|
||||||
|
public String getText() {
|
||||||
|
return textField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
public AddressFactory getAddressFactory() {
|
public AddressFactory getAddressFactory() {
|
||||||
return addrFactory;
|
return addrFactory;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.app.util.AddressInput;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link Address} types. In order to parse and create Address types, an
|
||||||
|
* {@link AddressFactory} is required when defining this type. As a convenience, it can
|
||||||
|
* be constructed with a {@link Program}, in which case it will use the AddressFactory from
|
||||||
|
* that program.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class AddressValue extends AbstractValue<Address> {
|
||||||
|
|
||||||
|
private AddressInput field;
|
||||||
|
private AddressFactory addressFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an AddressValue with an optional default value and uses the {@link AddressFactory}
|
||||||
|
* from the given program.
|
||||||
|
* @param name the name of this value
|
||||||
|
* @param defaultValue an optional default value
|
||||||
|
* @param program the program whose AddressFactory will be used to create Addresses.
|
||||||
|
*/
|
||||||
|
public AddressValue(String name, Address defaultValue, Program program) {
|
||||||
|
this(name, defaultValue, program.getAddressFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an AddressValue with an optional default value.
|
||||||
|
* @param name the name of this value
|
||||||
|
* @param defaultValue an optional default value
|
||||||
|
* @param factory the AddressFactory that will be used to create Addresses.
|
||||||
|
*/
|
||||||
|
public AddressValue(String name, Address defaultValue, AddressFactory factory) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.addressFactory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (field == null) {
|
||||||
|
field = new AddressInput();
|
||||||
|
field.setAddressFactory(addressFactory);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() throws ValuesMapParseException {
|
||||||
|
Address address = field.getAddress();
|
||||||
|
if (address == null && field.hasInput()) {
|
||||||
|
throw new ValuesMapParseException(getName(), "Address",
|
||||||
|
"Could not parse \"" + field.getText() + "\".");
|
||||||
|
}
|
||||||
|
setValue(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
Address v = getValue();
|
||||||
|
if (v == null) {
|
||||||
|
field.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
field.setAddress(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Address fromString(String valueString) {
|
||||||
|
Address address = addressFactory.getAddress(valueString);
|
||||||
|
if (address == null) {
|
||||||
|
throw new IllegalArgumentException("Invalid address string: " + valueString);
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import docking.Tool;
|
||||||
|
import docking.widgets.values.GValuesMap;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends GValuesMap to add Ghidra specific types such as Address and Program
|
||||||
|
*/
|
||||||
|
public class GhidraValuesMap extends GValuesMap {
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Define Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type {@link Address} with no default value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param program the program used to get an {@link AddressFactory} for parsing addresses
|
||||||
|
* @return the new AddressValue that was defined.
|
||||||
|
*/
|
||||||
|
public AddressValue defineAddress(String name, Program program) {
|
||||||
|
checkDup(name);
|
||||||
|
AddressValue value = new AddressValue(name, null, program);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type {@link Address}
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue an option default value
|
||||||
|
* @param program the program used to get an {@link AddressFactory} for parsing addresses
|
||||||
|
* @return the new AddressValue that was defined.
|
||||||
|
*/
|
||||||
|
public AddressValue defineAddress(String name, Address defaultValue, Program program) {
|
||||||
|
checkDup(name);
|
||||||
|
AddressValue value = new AddressValue(name, defaultValue, program);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type {@link Address}
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue an option default value
|
||||||
|
* @param factory the {@link AddressFactory} used to parse addresses
|
||||||
|
* @return the new AddressValue that was defined.
|
||||||
|
*/
|
||||||
|
public AddressValue defineAddress(String name, Address defaultValue, AddressFactory factory) {
|
||||||
|
checkDup(name);
|
||||||
|
AddressValue value = new AddressValue(name, defaultValue, factory);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type LanguageCompilerSpecPair (folders in a Ghidra project).
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value (can be null)
|
||||||
|
* @return the new ProjectFolderValue that was defined
|
||||||
|
*/
|
||||||
|
public LanguageValue defineLanguage(String name, LanguageCompilerSpecPair defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
LanguageValue value = new LanguageValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Program. This method opens programs using the given
|
||||||
|
* consumer and must be properly released when it is no longer needed. This is true
|
||||||
|
* even if the program is also opened in the tool.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param consumer the consumer to be used to open the program
|
||||||
|
* @param tool if non-null, the program will also be opened in the given tool
|
||||||
|
* @return the user-selected Program if a program was
|
||||||
|
* not selected or null. NOTE: It is very important that the program instance
|
||||||
|
* returned by this method ALWAYS be properly released from the consumer when no
|
||||||
|
* longer needed (i.e., {@code program.release(consumer) } - failure to
|
||||||
|
* properly release the program may result in improper project disposal. If the program was
|
||||||
|
* also opened in the tool, the tool will be a second consumer responsible for its
|
||||||
|
* own release.
|
||||||
|
*/
|
||||||
|
public ProgramValue defineProgram(String name, Object consumer, Tool tool) {
|
||||||
|
return defineProgram(name, null, consumer, tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Program.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @param consumer the consumer to be used to open the program
|
||||||
|
* @param tool if non-null, the program will also be opened in the given tool
|
||||||
|
* @return the new ProgramValue that was defined
|
||||||
|
*/
|
||||||
|
public ProgramValue defineProgram(String name, Program defaultValue, Object consumer,
|
||||||
|
Tool tool) {
|
||||||
|
checkDup(name);
|
||||||
|
ProgramValue value = new ProgramValue(name, defaultValue, consumer, tool);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type DomainFile (files in a Ghidra project).
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new ProjectFileValue that was defined
|
||||||
|
*/
|
||||||
|
public ProjectFileValue defineProjectFile(String name) {
|
||||||
|
return defineProjectFile(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type DomainFile (files in a Ghidra project).
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new ProjectFileValue that was defined
|
||||||
|
*/
|
||||||
|
public ProjectFileValue defineProjectFile(String name, DomainFile defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
ProjectFileValue value = new ProjectFileValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type DomainFolder (folders in a Ghidra project).
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new ProjectFolderValue that was defined
|
||||||
|
*/
|
||||||
|
public ProjectFolderValue defineProjectFolder(String name) {
|
||||||
|
return defineProjectFolder(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type DomainFolder (files in a Ghidra project).
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value (can be null)
|
||||||
|
* @return the new ProjectFolderValue that was defined
|
||||||
|
*/
|
||||||
|
public ProjectFolderValue defineProjectFolder(String name, DomainFolder defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
ProjectFolderValue value = new ProjectFolderValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Address} value for the given name.
|
||||||
|
* @param name the name of a previously defined Address value
|
||||||
|
* @return the Address
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as an Address type
|
||||||
|
*/
|
||||||
|
public Address getAddress(String name) {
|
||||||
|
AddressValue addressValue = getValue(name, AddressValue.class, "Address");
|
||||||
|
return addressValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Get Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
/**
|
||||||
|
* Gets the Language ({@link LanguageCompilerSpecPair}) value for the given name.
|
||||||
|
* @param name the name of a previously defined language value
|
||||||
|
* @return the language value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a language type
|
||||||
|
*/
|
||||||
|
public LanguageCompilerSpecPair getLanguage(String name) {
|
||||||
|
LanguageValue value = getValue(name, LanguageValue.class, "Language");
|
||||||
|
return value.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Program} value for the given name.
|
||||||
|
* @param name the name of a previously defined project folder value
|
||||||
|
* @return the project folder value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a project folder type
|
||||||
|
*/
|
||||||
|
public Program getProgram(String name) {
|
||||||
|
ProgramValue programValue = getValue(name, ProgramValue.class, "Program");
|
||||||
|
return programValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the project file ({@link DomainFile}) value for the given name.
|
||||||
|
* @param name the name of a previously defined project file value
|
||||||
|
* @return the project file value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a project file type
|
||||||
|
*/
|
||||||
|
public DomainFile getProjectFile(String name) {
|
||||||
|
ProjectFileValue domainFileValue = getValue(name, ProjectFileValue.class, "Domain File");
|
||||||
|
return domainFileValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the project folder ({@link DomainFolder}) value for the given name.
|
||||||
|
* @param name the name of a previously defined project folder value
|
||||||
|
* @return the project folder value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a project folder type
|
||||||
|
*/
|
||||||
|
public DomainFolder getProjectFolder(String name) {
|
||||||
|
ProjectFolderValue domainFolderValue =
|
||||||
|
getValue(name, ProjectFolderValue.class, "Domain Folder");
|
||||||
|
return domainFolderValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Set Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the address value for the given name.
|
||||||
|
* @param name the name of the Address value that was previously defined
|
||||||
|
* @param address the address to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as an Address type
|
||||||
|
*/
|
||||||
|
public void setAddress(String name, Address address) {
|
||||||
|
AddressValue addressValue = getValue(name, AddressValue.class, "Address");
|
||||||
|
addressValue.setValue(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Language ({@link LanguageCompilerSpecPair}) value for the given name.
|
||||||
|
* @param name the name of the Language value that was previously defined
|
||||||
|
* @param value the Language to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a Language type
|
||||||
|
*/
|
||||||
|
public void setLanguage(String name, LanguageCompilerSpecPair value) {
|
||||||
|
LanguageValue languageValue = getValue(name, LanguageValue.class, "Language");
|
||||||
|
languageValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Program} value for the given name.
|
||||||
|
* @param name the name of the Program value that was previously defined
|
||||||
|
* @param program the Program to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a Program type
|
||||||
|
*/
|
||||||
|
public void setProgram(String name, Program program) {
|
||||||
|
ProgramValue programValue = getValue(name, ProgramValue.class, "Program");
|
||||||
|
programValue.setValue(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the project file {@link DomainFile} value for the given name.
|
||||||
|
* @param name the name of the project file value that was previously defined
|
||||||
|
* @param file the project file to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a project file type
|
||||||
|
*/
|
||||||
|
public void setProjectFile(String name, DomainFile file) {
|
||||||
|
ProjectFileValue domainFileValue = getValue(name, ProjectFileValue.class, "Domain File");
|
||||||
|
domainFileValue.setValue(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the project folder {@link DomainFolder} value for the given name.
|
||||||
|
* @param name the name of the project folder value that was previously defined
|
||||||
|
* @param folder the project folder to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a project folder type
|
||||||
|
*/
|
||||||
|
public void setProjectFolder(String name, DomainFolder folder) {
|
||||||
|
ProjectFolderValue domainFolderValue =
|
||||||
|
getValue(name, ProjectFolderValue.class, "Domain Folder");
|
||||||
|
domainFolderValue.setValue(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.app.script.SelectLanguageDialog;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for LanguageCompilerSpecPair types. The component for this class is a
|
||||||
|
* TextField with a browse button for bringing up a language/compiler chooser. It supports
|
||||||
|
* the concept of no value when the text field is empty. If it is not empty, the the contents
|
||||||
|
* must be one of the known valid language/compiler spec pairs.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class LanguageValue extends AbstractValue<LanguageCompilerSpecPair> {
|
||||||
|
private LangaugeValuePanel languagePanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new LanguageVlue with no value
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
|
public LanguageValue(String name) {
|
||||||
|
super(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new LanguageVlue with a given optional default value.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the optional default value
|
||||||
|
*/
|
||||||
|
public LanguageValue(String name, LanguageCompilerSpecPair defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (languagePanel == null) {
|
||||||
|
languagePanel = new LangaugeValuePanel(getName());
|
||||||
|
}
|
||||||
|
return languagePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() throws ValuesMapParseException {
|
||||||
|
setValue(languagePanel.getLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
languagePanel.setLanguage(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LanguageCompilerSpecPair fromString(String valueString) {
|
||||||
|
try {
|
||||||
|
return parseLanguageCompileSpecPair(valueString);
|
||||||
|
}
|
||||||
|
catch (ValuesMapParseException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a LanguageCompilerSpecPair from a string.
|
||||||
|
*
|
||||||
|
* @param val The string to parse.
|
||||||
|
* @return The LanguageCompilerSpecPair parsed from a string or null if the string does
|
||||||
|
* not parse to a known language-compiler pair.
|
||||||
|
* @throws ValuesMapParseException
|
||||||
|
*/
|
||||||
|
public LanguageCompilerSpecPair parseLanguageCompileSpecPair(String val)
|
||||||
|
throws ValuesMapParseException {
|
||||||
|
|
||||||
|
if (val.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Split on last colon to get separated languageID and compilerSpecID
|
||||||
|
int lastColon = val.lastIndexOf(':');
|
||||||
|
if (lastColon < 1) {
|
||||||
|
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
||||||
|
"Could not parse \"" + val + "\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<LanguageCompilerSpecPair> languages = getLanguagesCompilerPairs();
|
||||||
|
|
||||||
|
String langId = val.substring(0, lastColon);
|
||||||
|
String compilerId = val.substring(lastColon + 1);
|
||||||
|
|
||||||
|
LanguageCompilerSpecPair storedLCS = new LanguageCompilerSpecPair(langId, compilerId);
|
||||||
|
if (!languages.contains(storedLCS)) {
|
||||||
|
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
||||||
|
"Unknown language/Compiler Pair for \"" + val + "\"");
|
||||||
|
}
|
||||||
|
return storedLCS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<LanguageCompilerSpecPair> getLanguagesCompilerPairs() {
|
||||||
|
Set<LanguageCompilerSpecPair> languages = new HashSet<>();
|
||||||
|
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||||
|
List<LanguageDescription> descriptions = languageService.getLanguageDescriptions(false);
|
||||||
|
for (LanguageDescription description : descriptions) {
|
||||||
|
Collection<CompilerSpecDescription> csDescriptions =
|
||||||
|
description.getCompatibleCompilerSpecDescriptions();
|
||||||
|
for (CompilerSpecDescription csDescription : csDescriptions) {
|
||||||
|
languages.add(new LanguageCompilerSpecPair(description.getLanguageID(),
|
||||||
|
csDescription.getCompilerSpecID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LangaugeValuePanel extends JPanel {
|
||||||
|
private JTextField textField;
|
||||||
|
private JButton browseButton;
|
||||||
|
|
||||||
|
public LangaugeValuePanel(String name) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
setName(name);
|
||||||
|
textField = new JTextField(20);
|
||||||
|
browseButton = new BrowseButton();
|
||||||
|
browseButton.addActionListener(e -> showLanguageDialog());
|
||||||
|
add(textField, BorderLayout.CENTER);
|
||||||
|
add(browseButton, BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LanguageCompilerSpecPair getLanguage() throws ValuesMapParseException {
|
||||||
|
return parseLanguageCompileSpecPair(textField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(LanguageCompilerSpecPair value) {
|
||||||
|
String text = value == null ? "" : value.toString();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLanguageDialog() {
|
||||||
|
SelectLanguageDialog dialog = new SelectLanguageDialog("Select Language", "Ok");
|
||||||
|
|
||||||
|
try {
|
||||||
|
dialog.setSelectedLanguage(getLanguage());
|
||||||
|
}
|
||||||
|
catch (ValuesMapParseException e) {
|
||||||
|
// we are just trying to initialize dialog, so don't care at this time
|
||||||
|
}
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
LanguageCompilerSpecPair selectedLanguage = dialog.getSelectedLanguage();
|
||||||
|
|
||||||
|
if (selectedLanguage != null) {
|
||||||
|
textField.setText(selectedLanguage.toString());
|
||||||
|
}
|
||||||
|
dialog.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new File(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String val) {
|
||||||
|
textField.setText(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import docking.Tool;
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link Program}s. The editor component consists of the {@link JTextField} and
|
||||||
|
* a browse button for bringing up a {@link DataTreeDialog} for picking programs from the
|
||||||
|
* current project.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class ProgramValue extends AbstractValue<Program> {
|
||||||
|
|
||||||
|
private ProjectBrowserPanel domainFilePanel;
|
||||||
|
private Tool tool;
|
||||||
|
private Object consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct for ProgramValue
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param consumer the program consumer to be used to open a program
|
||||||
|
* @param tool if non null, the program will also be opened in this tool
|
||||||
|
*/
|
||||||
|
public ProgramValue(String name, Object consumer, Tool tool) {
|
||||||
|
this(name, null, consumer, tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct for ProgramValue
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the program to use as the default value
|
||||||
|
* @param consumer the program consumer to be used to open a program
|
||||||
|
* @param tool if non null, the program will also be opened in this tool
|
||||||
|
*/
|
||||||
|
public ProgramValue(String name, Program defaultValue, Object consumer, Tool tool) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.consumer = consumer;
|
||||||
|
this.tool = tool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (domainFilePanel == null) {
|
||||||
|
domainFilePanel = new ProjectBrowserPanel(getName(), false);
|
||||||
|
}
|
||||||
|
return domainFilePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() throws ValuesMapParseException {
|
||||||
|
if (domainFilePanel != null) {
|
||||||
|
DomainFile domainFile = domainFilePanel.getDomainFile();
|
||||||
|
if (domainFile == null) {
|
||||||
|
String text = domainFilePanel.getText();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
setValue(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new ValuesMapParseException(getName(), "Program",
|
||||||
|
"No file found for \"" + text + "\"");
|
||||||
|
}
|
||||||
|
Program program = openProgram(domainFile);
|
||||||
|
setValue(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Program openProgram(DomainFile domainFile) throws ValuesMapParseException {
|
||||||
|
if (domainFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<? extends DomainObject> domainObjectClass = domainFile.getDomainObjectClass();
|
||||||
|
if (!Program.class.isAssignableFrom(domainObjectClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Program program =
|
||||||
|
(Program) domainFile.getDomainObject(consumer, false, false, TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
if (tool != null && program != null) {
|
||||||
|
tool.getService(ProgramManager.class).openProgram(program);
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
catch (VersionException | CancelledException | IOException e) {
|
||||||
|
throw new ValuesMapParseException(getName(), "Program", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
Program program = getValue();
|
||||||
|
DomainFile df = program == null ? null : program.getDomainFile();
|
||||||
|
domainFilePanel.setDomainFile(df);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Program fromString(String valueString) {
|
||||||
|
DomainFile programFile = ProjectBrowserPanel.parseDomainFile(valueString);
|
||||||
|
if (programFile == null) {
|
||||||
|
throw new IllegalArgumentException("Could not find program " + valueString);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Program program = openProgram(programFile);
|
||||||
|
if (program == null) {
|
||||||
|
throw new IllegalArgumentException("Can't open program: " + valueString);
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
catch (ValuesMapParseException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(Program v) {
|
||||||
|
return v.getDomainFile().getPathname();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.framework.store.FileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used by Values that use the DataTreeDialog for picking DomainFiles and DomainFolders
|
||||||
|
*/
|
||||||
|
class ProjectBrowserPanel extends JPanel {
|
||||||
|
private JTextField textField;
|
||||||
|
private JButton browseButton;
|
||||||
|
private boolean selectFolders;
|
||||||
|
|
||||||
|
ProjectBrowserPanel(String name, boolean selectFolders) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.selectFolders = selectFolders;
|
||||||
|
setName(name);
|
||||||
|
textField = new JTextField(20);
|
||||||
|
browseButton = new BrowseButton();
|
||||||
|
browseButton.addActionListener(e -> showDomainFileChooser());
|
||||||
|
add(textField, BorderLayout.CENTER);
|
||||||
|
add(browseButton, BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainFile(DomainFile value) {
|
||||||
|
String text = value == null ? "" : value.getPathname();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainFolder(DomainFolder value) {
|
||||||
|
String text = value == null ? "" : value.getPathname();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDomainFileChooser() {
|
||||||
|
DataTreeDialog dialog = new DataTreeDialog(null, "Choose " + getName(),
|
||||||
|
selectFolders ? DataTreeDialog.CHOOSE_FOLDER : DataTreeDialog.OPEN);
|
||||||
|
dialog.show();
|
||||||
|
if (dialog.wasCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String text = selectFolders ? dialog.getDomainFolder().getPathname()
|
||||||
|
: dialog.getDomainFile().getPathname();
|
||||||
|
textField.setText(text);
|
||||||
|
dialog.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainFile getDomainFile() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parseDomainFile(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getText() {
|
||||||
|
return textField.getText().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainFolder getDomainFolder() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return parseDomainFolder("/");
|
||||||
|
}
|
||||||
|
return parseDomainFolder(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DomainFile parseDomainFile(String val) {
|
||||||
|
// Add the slash to make it an absolute path
|
||||||
|
if (!val.isEmpty() && val.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
|
val = FileSystem.SEPARATOR_CHAR + val;
|
||||||
|
}
|
||||||
|
Project activeProject = AppInfo.getActiveProject();
|
||||||
|
DomainFile df = activeProject.getProjectData().getFile(val);
|
||||||
|
if (df != null) {
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DomainFolder parseDomainFolder(String path) {
|
||||||
|
path = path.trim();
|
||||||
|
// Add the slash to make it an absolute path
|
||||||
|
if (path.isEmpty() || path.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
|
path = FileSystem.SEPARATOR_CHAR + path;
|
||||||
|
}
|
||||||
|
Project activeProject = AppInfo.getActiveProject();
|
||||||
|
DomainFolder df = activeProject.getProjectData().getFolder(path);
|
||||||
|
if (df != null) {
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.framework.store.FileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for project files ({@link DomainFile}). The editor component consists of the
|
||||||
|
* {@link JTextField} and a browse button for bringing up a {@link DataTreeDialog} for picking
|
||||||
|
* project files from the current project.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class ProjectFileValue extends AbstractValue<DomainFile> {
|
||||||
|
|
||||||
|
private ProjectBrowserPanel domainFilePanel;
|
||||||
|
|
||||||
|
public ProjectFileValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectFileValue(String name, DomainFile defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (domainFilePanel == null) {
|
||||||
|
domainFilePanel = new ProjectBrowserPanel(getName(), false);
|
||||||
|
}
|
||||||
|
return domainFilePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() throws ValuesMapParseException {
|
||||||
|
if (domainFilePanel != null) {
|
||||||
|
DomainFile domainFile = domainFilePanel.getDomainFile();
|
||||||
|
if (domainFile == null) {
|
||||||
|
String text = domainFilePanel.getText();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
setValue(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new ValuesMapParseException(getName(), "Project File",
|
||||||
|
"No file found for \"" + text + "\"");
|
||||||
|
}
|
||||||
|
setValue(domainFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
if (domainFilePanel != null) {
|
||||||
|
domainFilePanel.setDomainFile(getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DomainFile fromString(String valueString) {
|
||||||
|
DomainFile df = parseDomainFile(valueString);
|
||||||
|
if (df == null) {
|
||||||
|
throw new IllegalArgumentException("Can't find domain file: " + valueString);
|
||||||
|
}
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainFile parseDomainFile(String val) {
|
||||||
|
// Add the slash to make it an absolute path
|
||||||
|
if (!val.isEmpty() && val.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
|
val = FileSystem.SEPARATOR_CHAR + val;
|
||||||
|
}
|
||||||
|
Project activeProject = AppInfo.getActiveProject();
|
||||||
|
if (activeProject == null) {
|
||||||
|
throw new IllegalStateException("No Active Project!");
|
||||||
|
}
|
||||||
|
DomainFile df = activeProject.getProjectData().getFile(val);
|
||||||
|
if (df != null) {
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(DomainFile v) {
|
||||||
|
return v.getPathname();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 ghidra.features.base.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for project folders ({@link DomainFile}). The editor component consists of the
|
||||||
|
* {@link JTextField} and a browse button for bringing up a {@link DataTreeDialog} for picking
|
||||||
|
* project folders from the current project.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class ProjectFolderValue extends AbstractValue<DomainFolder> {
|
||||||
|
|
||||||
|
private ProjectBrowserPanel domainFilePanel;
|
||||||
|
|
||||||
|
public ProjectFolderValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectFolderValue(String name, DomainFolder defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (domainFilePanel == null) {
|
||||||
|
domainFilePanel = new ProjectBrowserPanel(getName(), true);
|
||||||
|
}
|
||||||
|
return domainFilePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() throws ValuesMapParseException {
|
||||||
|
if (domainFilePanel != null) {
|
||||||
|
DomainFolder domainFolder = domainFilePanel.getDomainFolder();
|
||||||
|
if (domainFolder == null) {
|
||||||
|
String text = domainFilePanel.getText();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
setValue(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new ValuesMapParseException(getName(), "Project Folder",
|
||||||
|
"No folder found for \"" + text + "\"");
|
||||||
|
}
|
||||||
|
setValue(domainFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
|
||||||
|
if (domainFilePanel != null) {
|
||||||
|
domainFilePanel.setDomainFolder(getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DomainFolder fromString(String valueString) {
|
||||||
|
DomainFolder df = ProjectBrowserPanel.parseDomainFolder(valueString);
|
||||||
|
if (df == null) {
|
||||||
|
throw new IllegalArgumentException("Can't find domain folder: " + valueString);
|
||||||
|
}
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(DomainFolder v) {
|
||||||
|
return v.getPathname();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -435,6 +435,14 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
Swing.runLater(() -> treePanel.selectRootDataFolder());
|
Swing.runLater(() -> treePanel.selectRootDataFolder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a folder in the tree.
|
||||||
|
* @param folder the folder to select
|
||||||
|
*/
|
||||||
|
public void selectFolder(DomainFolder folder) {
|
||||||
|
Swing.runLater(() -> treePanel.selectDomainFolder(folder));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select the node that corresponds to the given domain file.
|
* Select the node that corresponds to the given domain file.
|
||||||
* @param file the file
|
* @param file the file
|
||||||
|
|
|
@ -299,6 +299,9 @@ public class NewLanguagePanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setSelectedLcsPair(LanguageCompilerSpecPair lcsPair) {
|
public boolean setSelectedLcsPair(LanguageCompilerSpecPair lcsPair) {
|
||||||
|
if (lcsPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int index = tableModel.getFirstLcsPairIndex(lcsPair);
|
int index = tableModel.getFirstLcsPairIndex(lcsPair);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.DockingWindowManager;
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import docking.widgets.values.ValuesMapDialog;
|
||||||
|
import ghidra.features.base.values.GhidraValuesMap;
|
||||||
|
import ghidra.features.base.values.ProjectBrowserPanel;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
import ghidra.test.TestEnv;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public abstract class AbstractValueIntegrationTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
protected ValuesMapDialog dialog;
|
||||||
|
protected GhidraValuesMap values = new GhidraValuesMap();
|
||||||
|
|
||||||
|
protected TestEnv env;
|
||||||
|
protected DomainFolder rootFolder;
|
||||||
|
protected DomainFolder folder;
|
||||||
|
protected DomainFile fileA;
|
||||||
|
protected DomainFile fileB;
|
||||||
|
protected ProgramDB programA;
|
||||||
|
protected ProgramDB programB;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
env = new TestEnv();
|
||||||
|
Project project = env.getProject();
|
||||||
|
runSwing(() -> env.getFrontEndTool().setActiveProject(project));
|
||||||
|
rootFolder = project.getProjectData().getRootFolder();
|
||||||
|
folder = rootFolder.createFolder("A");
|
||||||
|
ProgramBuilder programBuilderA = new ProgramBuilder("A", ProgramBuilder._TOY);
|
||||||
|
ProgramBuilder programBuilderB = new ProgramBuilder("B", ProgramBuilder._TOY);
|
||||||
|
programA = programBuilderA.getProgram();
|
||||||
|
programB = programBuilderB.getProgram();
|
||||||
|
fileA = folder.createFile("A", programA, TaskMonitor.DUMMY);
|
||||||
|
fileB = folder.createFile("B", programB, TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
env.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showDialogOnSwingWithoutBlocking() {
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
dialog = new ValuesMapDialog("Test", null, values);
|
||||||
|
DockingWindowManager.showDialog(dialog);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
waitForDialogComponent(DialogComponentProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pressOk() {
|
||||||
|
JButton okButton = (JButton) getInstanceField("okButton", dialog);
|
||||||
|
runSwing(() -> okButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pressCancel() {
|
||||||
|
JButton okButton = (JButton) getInstanceField("cancelButton", dialog);
|
||||||
|
runSwing(() -> okButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setProjectFileOnProjectTree(AbstractValue<?> value, DomainFile file) {
|
||||||
|
ProjectBrowserPanel projectWidget = (ProjectBrowserPanel) value.getComponent();
|
||||||
|
pressButtonByName(projectWidget, "BrowseButton", false);
|
||||||
|
|
||||||
|
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
||||||
|
runSwing(() -> {
|
||||||
|
dataTreeDialog.selectDomainFile(file);
|
||||||
|
});
|
||||||
|
waitForSwing();
|
||||||
|
pressButtonByText(dataTreeDialog, "OK");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setProjectFolderOnProjectTree(AbstractValue<?> value, DomainFolder folder) {
|
||||||
|
ProjectBrowserPanel projectWidget = (ProjectBrowserPanel) value.getComponent();
|
||||||
|
pressButtonByName(projectWidget, "BrowseButton", false);
|
||||||
|
|
||||||
|
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
||||||
|
runSwing(() -> {
|
||||||
|
dataTreeDialog.selectFolder(folder);
|
||||||
|
});
|
||||||
|
waitForSwing();
|
||||||
|
pressButtonByText(dataTreeDialog, "OK");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTextOnComponent(AbstractValue<?> nameValue, String text) {
|
||||||
|
runSwing(() -> {
|
||||||
|
JTextField field = (JTextField) nameValue.getComponent();
|
||||||
|
field.setText(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AddressFactory createAddressFactory() {
|
||||||
|
GenericAddressSpace space1 = new GenericAddressSpace("A", 64, AddressSpace.TYPE_RAM, 0);
|
||||||
|
GenericAddressSpace space2 = new GenericAddressSpace("B", 64, AddressSpace.TYPE_RAM, 0);
|
||||||
|
return new DefaultAddressFactory(new AddressSpace[] { space1, space2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import ghidra.app.util.AddressInput;
|
||||||
|
import ghidra.features.base.values.AddressValue;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
|
||||||
|
public class AddressValueTest extends AbstractValueIntegrationTest {
|
||||||
|
private static final String NAME = "Start Address";
|
||||||
|
protected AddressFactory factory = createAddressFactory();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddressValueNoDefault() {
|
||||||
|
values.defineAddress(NAME, null, factory);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setAddress(NAME, addr(13));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(addr(13), values.getAddress(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddressValueWithDefault() {
|
||||||
|
values.defineAddress(NAME, addr(1), factory);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(addr(1), values.getAddress(NAME));
|
||||||
|
|
||||||
|
values.setAddress(NAME, addr(2));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(addr(2), values.getAddress(NAME));
|
||||||
|
|
||||||
|
values.setAddress(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
AddressValue value1 = new AddressValue(NAME, addr(0x123), factory);
|
||||||
|
AddressValue value2 = new AddressValue(NAME, null, factory);
|
||||||
|
assertEquals("A:00000123", value1.getAsText());
|
||||||
|
assertNull(value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
AddressValue v = new AddressValue(NAME, null, factory);
|
||||||
|
assertEquals(addr(0x123), v.setAsText("A:00000123"));
|
||||||
|
try {
|
||||||
|
v.setAsText("xdsf");
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineAddress(NAME, null, factory);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getAddress(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineAddress(NAME, null, factory);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnAddressInput(values.getAbstractValue(NAME), "2");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(addr(2), values.getAddress(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineAddress(NAME, addr(1), factory);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(addr(1), values.getAddress(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineAddress(NAME, addr(1), factory);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnAddressInput(values.getAbstractValue(NAME), "2");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(addr(2), values.getAddress(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address addr(int offset) {
|
||||||
|
return factory.getDefaultAddressSpace().getAddress(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTextOnAddressInput(AbstractValue<?> nameValue, String text) {
|
||||||
|
runSwing(() -> {
|
||||||
|
AddressInput addressInput = (AddressInput) nameValue.getComponent();
|
||||||
|
addressInput.setValue(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import ghidra.app.script.SelectLanguageDialog;
|
||||||
|
import ghidra.features.base.values.LanguageValue;
|
||||||
|
import ghidra.features.base.values.LanguageValue.LangaugeValuePanel;
|
||||||
|
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||||
|
|
||||||
|
public class LanguageValueTest extends AbstractValueIntegrationTest {
|
||||||
|
private static final String NAME = "Lang";
|
||||||
|
private static final LanguageCompilerSpecPair LANG1 =
|
||||||
|
new LanguageCompilerSpecPair("6502:LE:16:default", "default");
|
||||||
|
private static final LanguageCompilerSpecPair LANG2 =
|
||||||
|
new LanguageCompilerSpecPair("ARM:BE:32:v7", "default");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLanguageValueNoDefault() {
|
||||||
|
values.defineLanguage(NAME, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setLanguage(NAME, LANG1);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(LANG1, values.getLanguage(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLanguageValueWithDefault() {
|
||||||
|
values.defineLanguage(NAME, LANG1);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(LANG1, values.getLanguage(NAME));
|
||||||
|
|
||||||
|
values.setLanguage(NAME, LANG2);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(LANG2, values.getLanguage(NAME));
|
||||||
|
|
||||||
|
values.setLanguage(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
LanguageValue value1 = new LanguageValue(NAME);
|
||||||
|
LanguageValue value2 = new LanguageValue(NAME, LANG1);
|
||||||
|
assertNull(value1.getAsText());
|
||||||
|
assertEquals("6502:LE:16:default:default", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
LanguageValue v = new LanguageValue(NAME);
|
||||||
|
assertEquals(LANG1, v.setAsText("6502:LE:16:default:default"));
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineLanguage(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getLanguage(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineLanguage(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setLanguage(values.getAbstractValue(NAME), LANG1);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(LANG1, values.getLanguage(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithBadDialogInput() {
|
||||||
|
values.defineLanguage(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setLanguage(values.getAbstractValue(NAME), "asdfa");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(dialog.isShowing());
|
||||||
|
pressCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineLanguage(NAME, LANG1);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(LANG1, values.getLanguage(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineLanguage(NAME, LANG1);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setLanguage(values.getAbstractValue(NAME), LANG2);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(LANG2, values.getLanguage(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setLanguage(AbstractValue<?> nameValue, LanguageCompilerSpecPair lang) {
|
||||||
|
LangaugeValuePanel languageWidget = (LangaugeValuePanel) nameValue.getComponent();
|
||||||
|
pressButtonByName(languageWidget, "BrowseButton", false);
|
||||||
|
SelectLanguageDialog langDialog = waitForDialogComponent(SelectLanguageDialog.class);
|
||||||
|
runSwing(() -> {
|
||||||
|
langDialog.setSelectedLanguage(lang);
|
||||||
|
});
|
||||||
|
|
||||||
|
pressButtonByText(langDialog, "Ok");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setLanguage(AbstractValue<?> nameValue, String val) {
|
||||||
|
LangaugeValuePanel languageWidget = (LangaugeValuePanel) nameValue.getComponent();
|
||||||
|
runSwing(() -> {
|
||||||
|
languageWidget.setText(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.features.base.values.ProgramValue;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
public class ProgramValueTest extends AbstractValueIntegrationTest {
|
||||||
|
private static final String NAME = "Program";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgramValueNoDefault() {
|
||||||
|
values.defineProgram(NAME, this, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setProgram(NAME, programA);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(programA, values.getProgram(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgramValueWithDefault() {
|
||||||
|
values.defineProgram(NAME, programA, this, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(programA, values.getProgram(NAME));
|
||||||
|
|
||||||
|
values.setProgram(NAME, programB);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(programB, values.getProgram(NAME));
|
||||||
|
|
||||||
|
values.setProgram(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
ProgramValue value1 = new ProgramValue(NAME, this, null);
|
||||||
|
ProgramValue value2 = new ProgramValue(NAME, programA, this, null);
|
||||||
|
assertNull(value1.getAsText());
|
||||||
|
assertEquals("/A/A", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
ProgramValue v = new ProgramValue(NAME, this, null);
|
||||||
|
assertEquals(programA, v.setAsText("/A/A"));
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
v.setAsText("/z/z/t");
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProgram(NAME, this, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getProgram(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineProgram(NAME, this, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile());
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(programA, values.getProgram(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProgram(NAME, programA, this, null);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(programA, values.getProgram(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineProgram(NAME, programA, this, null);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programB.getDomainFile());
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(programB, values.getProgram(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOpenProgramInTool() {
|
||||||
|
PluginTool tool = env.createDefaultTool();
|
||||||
|
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||||
|
Program[] allOpenPrograms = programManagerService.getAllOpenPrograms();
|
||||||
|
assertEquals(0, allOpenPrograms.length);
|
||||||
|
|
||||||
|
values.defineProgram(NAME, this, tool);
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile());
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
allOpenPrograms = programManagerService.getAllOpenPrograms();
|
||||||
|
assertEquals(1, allOpenPrograms.length);
|
||||||
|
assertEquals(programA, allOpenPrograms[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.features.base.values.ProjectFileValue;
|
||||||
|
|
||||||
|
public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
|
private static final String NAME = "Project File";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectFileValueNoDefault() {
|
||||||
|
values.defineProjectFile(NAME, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setProjectFile(NAME, fileA);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(fileA, values.getProjectFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectFileValueWithDefault() {
|
||||||
|
values.defineProjectFile(NAME, fileA);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(fileA, values.getProjectFile(NAME));
|
||||||
|
|
||||||
|
values.setProjectFile(NAME, fileB);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(fileB, values.getProjectFile(NAME));
|
||||||
|
|
||||||
|
values.setProjectFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
ProjectFileValue value1 = new ProjectFileValue(NAME);
|
||||||
|
ProjectFileValue value2 = new ProjectFileValue(NAME, fileA);
|
||||||
|
assertNull(value1.getAsText());
|
||||||
|
assertEquals("/A/A", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
ProjectFileValue v = new ProjectFileValue(NAME);
|
||||||
|
assertEquals(fileA, v.setAsText("/A/A"));
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
v.setAsText("/zasd/asdfas");
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProjectFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getProjectFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineProjectFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), fileA);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(fileA, values.getProjectFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProjectFile(NAME, fileA);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(fileA, values.getProjectFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineProjectFile(NAME, fileA);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), fileB);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(fileB, values.getProjectFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.features.base.values.ProjectFolderValue;
|
||||||
|
|
||||||
|
public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
|
private static final String NAME = "Project File";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectFolderValueNoDefault() {
|
||||||
|
values.defineProjectFolder(NAME, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setProjectFolder(NAME, folder);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(folder, values.getProjectFolder(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectFolderValueWithDefault() {
|
||||||
|
values.defineProjectFolder(NAME, rootFolder);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(rootFolder, values.getProjectFolder(NAME));
|
||||||
|
|
||||||
|
values.setProjectFolder(NAME, folder);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(folder, values.getProjectFolder(NAME));
|
||||||
|
|
||||||
|
values.setProjectFolder(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
ProjectFolderValue value1 = new ProjectFolderValue(NAME);
|
||||||
|
ProjectFolderValue value2 = new ProjectFolderValue(NAME, folder);
|
||||||
|
assertNull(value1.getAsText());
|
||||||
|
assertEquals("/A", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
ProjectFolderValue v = new ProjectFolderValue(NAME);
|
||||||
|
assertEquals(folder, v.setAsText("/A"));
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
v.setAsText("/zasd/asdfas");
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProjectFolder(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
// usually, this would have no value, but the root folder is such an obvious value
|
||||||
|
// in nothing is entered, we use that.
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(rootFolder, values.getProjectFolder(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineProjectFolder(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folder);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(folder, values.getProjectFolder(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineProjectFolder(NAME, folder);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(folder, values.getProjectFolder(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineProjectFolder(NAME, rootFolder);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folder);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(folder, values.getProjectFolder(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import ghidra.app.util.AddressInput;
|
||||||
|
|
||||||
|
public class ValuesMapDialogParseErrorTest extends AbstractValueIntegrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseErrorBlocksDialogFromClosing() {
|
||||||
|
values.defineString("Name");
|
||||||
|
values.defineAddress("Start", programA);
|
||||||
|
values.defineInt("Size");
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnAddressInput(values.getAbstractValue("Start"), "sdfasf");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(dialog.isShowing());
|
||||||
|
assertTrue(dialog.getStatusText().startsWith("Error"));
|
||||||
|
setTextOnAddressInput(values.getAbstractValue("Start"), "0");
|
||||||
|
pressOk();
|
||||||
|
assertFalse(dialog.isShowing());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTextOnAddressInput(AbstractValue<?> nameValue, String text) {
|
||||||
|
runSwing(() -> {
|
||||||
|
AddressInput addressInput = (AddressInput) nameValue.getComponent();
|
||||||
|
addressInput.setValue(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -222,7 +222,7 @@ public class ScrollableTextArea extends JScrollPane {
|
||||||
* used by all constructors to finish initialization of the object
|
* used by all constructors to finish initialization of the object
|
||||||
*/
|
*/
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
textArea.setLineWrap(false);
|
textArea.setLineWrap(true);
|
||||||
this.setAutoscrolls(true);
|
this.setAutoscrolls(true);
|
||||||
this.setViewportView(textArea);
|
this.setViewportView(textArea);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for defined name/values in a {@link GValuesMap} and whose values can be
|
||||||
|
* edited in the {@link ValuesMapDialog}. Its main purpose is to provide a JComponent for
|
||||||
|
* editing the value. Generally, objects of this type can be in one of two states: having a value
|
||||||
|
* or not. This can be useful for validating the dialog input values to ensure the user enters
|
||||||
|
* a value.
|
||||||
|
* <P>
|
||||||
|
* There are two situations where parse/conversion exceptions can occur in subclass implementations.
|
||||||
|
* One is the {@link #setAsText(String)} method. The subclass should catch any specific expected
|
||||||
|
* exception when parsing the string and convert it to an IllegalArgumentException. The other method
|
||||||
|
* is the {@link #updateValueFromComponent()} method which may also need to parse string data. In
|
||||||
|
* this case any expected exception should be converted to {@link ValuesMapParseException}. This
|
||||||
|
* is the only exception type the dialog will be trapping and displaying error messages for in the
|
||||||
|
* {@link ValuesMapDialog}. Any other type of exception will be considered unexpected and a
|
||||||
|
* programing error and will be eventally be handled by the default application error handler.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the value stored and edited by this class
|
||||||
|
*/
|
||||||
|
public abstract class AbstractValue<T> {
|
||||||
|
private final String name;
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that assigned a name and optional initial value for this object.
|
||||||
|
* @param name the name associated with this value.
|
||||||
|
* @param defaultValue an optional initial value for this object
|
||||||
|
*/
|
||||||
|
protected AbstractValue(String name, T defaultValue) {
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
|
this.value = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this value object.
|
||||||
|
* @return the name of this value object
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value currently assigned to this object.
|
||||||
|
* @return the value currently assigned to this object (may be null)
|
||||||
|
*/
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for this object.
|
||||||
|
* @param value the value to set for this object (may be null)
|
||||||
|
*/
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the T value from the given AbstractValue to this AbstractValue.
|
||||||
|
* @param other the AbstractValue to copy from
|
||||||
|
*/
|
||||||
|
public void copyValue(AbstractValue<T> other) {
|
||||||
|
setValue(other.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the value is non-null.
|
||||||
|
* @return true if the value is non-null
|
||||||
|
*/
|
||||||
|
public boolean hasValue() {
|
||||||
|
return value != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for this object from the given string. If this object can not succesfully
|
||||||
|
* parse the string, an exception will be thrown.
|
||||||
|
* @param valueString the string to be parsed into the type for this object
|
||||||
|
* @return The value resulting from parsing the string value
|
||||||
|
* @throws IllegalArgumentException if the string can not be parsed into a value of type T
|
||||||
|
*/
|
||||||
|
public T setAsText(String valueString) {
|
||||||
|
if (valueString == null) {
|
||||||
|
throw new IllegalArgumentException("Value string can not be null!");
|
||||||
|
}
|
||||||
|
value = fromString(valueString);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation for the value. It is expected that the string returned
|
||||||
|
* from this method can be parsed by the corresponding {@link #setAsText(String)} method. If the
|
||||||
|
* value of this object is null, null will be returned.
|
||||||
|
* @return a string representation for the value or null if the value is null
|
||||||
|
*/
|
||||||
|
public String getAsText() {
|
||||||
|
return value == null ? null : toString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String toString(T t) {
|
||||||
|
return t.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JComponent for entering or editing a value of this type.
|
||||||
|
* @return a JComponent for entering or editing a value of this type.
|
||||||
|
*/
|
||||||
|
public abstract JComponent getComponent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the stored value for this object to be updated based on the state of the
|
||||||
|
* JComponent returned from {@link #getComponent()}
|
||||||
|
* @throws ValuesMapParseException if an error occurs trying update the value from a
|
||||||
|
* component. This usually is a result of trying to parse a string value.
|
||||||
|
*/
|
||||||
|
protected abstract void updateValueFromComponent() throws ValuesMapParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the JComponent returned from {@link #getComponent()} to represent the current
|
||||||
|
* value of this object.
|
||||||
|
*/
|
||||||
|
protected abstract void updateComponentFromValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string into a value of type T
|
||||||
|
* @param valueString the string to parse
|
||||||
|
* @return a value of type T
|
||||||
|
*/
|
||||||
|
protected abstract T fromString(String valueString);
|
||||||
|
|
||||||
|
}
|
|
@ -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 docking.widgets.values;
|
||||||
|
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link Boolean} types. Boolean types use a {@link JCheckBox} for displaying and
|
||||||
|
* modifying values. Because the checkBox is always either checked or unchecked,
|
||||||
|
* BooleanValues don't support the concept of having no value.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly. */
|
||||||
|
public class BooleanValue extends AbstractValue<Boolean> {
|
||||||
|
|
||||||
|
private JCheckBox checkBox;
|
||||||
|
|
||||||
|
BooleanValue(String name, boolean defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (checkBox == null) {
|
||||||
|
checkBox = new JCheckBox();
|
||||||
|
}
|
||||||
|
return checkBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
setValue(checkBox.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
checkBox.setSelected(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean fromString(String valueString) {
|
||||||
|
return Boolean.parseBoolean(valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 docking.widgets.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import docking.widgets.combobox.GComboBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for selecting from a restricted set of {@link String}s. ChoiceValues uses a
|
||||||
|
* {@link GComboBox} for the editor component.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly. */
|
||||||
|
public class ChoiceValue extends AbstractValue<String> {
|
||||||
|
|
||||||
|
private String[] choices;
|
||||||
|
private GComboBox<String> combo;
|
||||||
|
|
||||||
|
ChoiceValue(String name, String defaultValue, String... choices) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.choices = choices;
|
||||||
|
if (defaultValue != null && !isValidChoice(defaultValue)) {
|
||||||
|
throw new IllegalArgumentException("Default value is not one of the valid choices!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (combo == null) {
|
||||||
|
combo = new GComboBox<String>(choices);
|
||||||
|
}
|
||||||
|
return combo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
setValue((String) combo.getSelectedItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
combo.setSelectedItem(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String fromString(String valueString) {
|
||||||
|
if (valueString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isValidChoice(valueString)) {
|
||||||
|
return valueString;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(valueString + " is not a valid choice!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidChoice(String valueString) {
|
||||||
|
for (String choice : choices) {
|
||||||
|
if (choice.equals(valueString)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.FloatingPointTextField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link Double} types. This value uses a {@link FloatingPointTextField} as it's
|
||||||
|
* editor component. It supports the concept of no value, if the text field is empty.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class DoubleValue extends AbstractValue<Double> {
|
||||||
|
private FloatingPointTextField field;
|
||||||
|
|
||||||
|
public DoubleValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleValue(String name, Double defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (field == null) {
|
||||||
|
field = new FloatingPointTextField(20);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
String text = field.getText();
|
||||||
|
|
||||||
|
// special case where user didn't enter a value on a string field that was defined without
|
||||||
|
// a value
|
||||||
|
if (getValue() == null && text.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(field.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
Double value = getValue();
|
||||||
|
if (value == null) {
|
||||||
|
field.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double fromString(String valueString) {
|
||||||
|
return Double.parseDouble(valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link File} types. FileValues can be used for either file or directory values,
|
||||||
|
* depending on the constructor options. The editor component uses a {@link JTextField} with
|
||||||
|
* a browse button for bringing up a {@link GhidraFileChooser} for picking files or directories.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class FileValue extends AbstractValue<File> {
|
||||||
|
private GhidraFileChooserMode chooserMode;
|
||||||
|
private File startingDir;
|
||||||
|
private FileValuePanel filePanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FileValue that expects its value to represent a file and not a directory.
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
|
public FileValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FileValue that expects its value to represent a file and not a directory.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the optional default File value.
|
||||||
|
*/
|
||||||
|
public FileValue(String name, File defaultValue) {
|
||||||
|
this(name, defaultValue, null, GhidraFileChooserMode.FILES_AND_DIRECTORIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FileValue that could represent either a File or Directory, depending on the
|
||||||
|
* mode value.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the optional default File value. If non-null this can be either a
|
||||||
|
* file or directory, but it should match the given {@link GhidraFileChooserMode}
|
||||||
|
* @param startingDir an optional directory specifying where the FileChooser should intialize
|
||||||
|
* its starting selected directory.
|
||||||
|
* @param mode the {@link GhidraFileChooserMode} used to indicate if this File represents a
|
||||||
|
* file or directory. It will put the GhidraFileChooser in a mode for choosing files or
|
||||||
|
* directories.
|
||||||
|
*/
|
||||||
|
public FileValue(String name, File defaultValue, File startingDir, GhidraFileChooserMode mode) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.chooserMode = mode;
|
||||||
|
this.startingDir = startingDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (filePanel == null) {
|
||||||
|
filePanel = new FileValuePanel(getName());
|
||||||
|
}
|
||||||
|
return filePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateValueFromComponent() {
|
||||||
|
setValue(filePanel.getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateComponentFromValue() {
|
||||||
|
filePanel.setValue(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File fromString(String valueString) {
|
||||||
|
return new File(valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// not private so that tests can access this class
|
||||||
|
class FileValuePanel extends JPanel {
|
||||||
|
private JTextField textField;
|
||||||
|
private JButton browseButton;
|
||||||
|
|
||||||
|
public FileValuePanel(String name) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
setName(name);
|
||||||
|
textField = new JTextField(20);
|
||||||
|
browseButton = new BrowseButton();
|
||||||
|
browseButton.addActionListener(e -> showFileChooser());
|
||||||
|
add(textField, BorderLayout.CENTER);
|
||||||
|
add(browseButton, BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(File value) {
|
||||||
|
String text = value == null ? "" : value.toString();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFileChooser() {
|
||||||
|
GhidraFileChooser chooser = new GhidraFileChooser(null);
|
||||||
|
chooser.setSelectedFile(getFile());
|
||||||
|
chooser.setTitle("Choose " + getName());
|
||||||
|
chooser.setFileSelectionMode(chooserMode);
|
||||||
|
if (startingDir != null) {
|
||||||
|
chooser.setCurrentDirectory(startingDir);
|
||||||
|
}
|
||||||
|
File selectedFile = chooser.getSelectedFile();
|
||||||
|
if (selectedFile != null) {
|
||||||
|
textField.setText(selectedFile.toString());
|
||||||
|
}
|
||||||
|
chooser.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new File(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,556 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
|
import ghidra.util.StatusListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for defining, storing, and retrieving groups of values of various types. The intended use
|
||||||
|
* is to create a ValuesMap, define some named values, and then invoke the ValuesMapDialog to allow
|
||||||
|
* the user to fill in values for the defined values. It also has a rich set of convenience methods
|
||||||
|
* for adding predefined value types to the map. Users can also directly add custom value types by
|
||||||
|
* using the {@link #addValue(AbstractValue)} method.
|
||||||
|
*/
|
||||||
|
public class GValuesMap {
|
||||||
|
|
||||||
|
protected Map<String, AbstractValue<?>> valuesMap = new LinkedHashMap<>();
|
||||||
|
private ValuesMapValidator validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of the AbstractValues defined in this ValuesMap.
|
||||||
|
* @return a collection of the AbstractValues defined in this ValuesMap.
|
||||||
|
*/
|
||||||
|
public Collection<AbstractValue<?>> getValues() {
|
||||||
|
return valuesMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an AbstractValue to this ValuesMap. This is a way to add a custom AbstractValue that
|
||||||
|
* doesn't have a convenience method for a predefine value type.
|
||||||
|
* @param value the AbstractValue to add to this ValuesMap
|
||||||
|
* @return returns the added value
|
||||||
|
*/
|
||||||
|
public AbstractValue<?> addValue(AbstractValue<?> value) {
|
||||||
|
String name = value.getName();
|
||||||
|
checkDup(name);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a {@link ValuesMapValidator}. If set, this will be called when the user presses the
|
||||||
|
* "Ok" button on the {@link ValuesMapDialog}. If the validator passes (returns true), then
|
||||||
|
* the dialog will close and return the user values. Otherwise, the dialog will display the
|
||||||
|
* error message (via the {@link StatusListener} in the
|
||||||
|
* {@link ValuesMapValidator#validate(GValuesMap, StatusListener)} call) and remain open.
|
||||||
|
* @param validator the validator to be called before returning from the dialog
|
||||||
|
*/
|
||||||
|
public void setValidator(ValuesMapValidator validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The call to validate the data using the {@link ValuesMapValidator} set in the
|
||||||
|
* {@link #setValidator(ValuesMapValidator)} method. If no validator has been set,
|
||||||
|
* this method will return true.
|
||||||
|
* @param listener The {@link StatusListener} for reporting an error message.
|
||||||
|
* @return true if the validator passes or no validator has been set.
|
||||||
|
*/
|
||||||
|
public boolean isValid(StatusListener listener) {
|
||||||
|
if (validator != null) {
|
||||||
|
return validator.validate(this, listener);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates each value in this ValuesMap from its corresponding JComponent.
|
||||||
|
* @throws ValuesMapParseException if any value encountered an error trying to update its
|
||||||
|
* value from the editor component.
|
||||||
|
*/
|
||||||
|
public void updateFromComponents() throws ValuesMapParseException {
|
||||||
|
for (AbstractValue<?> inputValue : valuesMap.values()) {
|
||||||
|
inputValue.updateValueFromComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the AbstractValue for the given value name.
|
||||||
|
* @param name the name for which to get the AbstractValue
|
||||||
|
* @return the AbstractValue for the given value name.
|
||||||
|
*/
|
||||||
|
public AbstractValue<?> getAbstractValue(String name) {
|
||||||
|
return valuesMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a defined value for the given name.
|
||||||
|
* @param name the name of the value to check for
|
||||||
|
* @return true if there is a defined value for the given name.
|
||||||
|
*/
|
||||||
|
public boolean isDefined(String name) {
|
||||||
|
return valuesMap.containsKey(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the value defined for the given name has a non-null value.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @return true if the value defined for the given name has a non-null value.
|
||||||
|
*/
|
||||||
|
public boolean hasValue(String name) {
|
||||||
|
AbstractValue<?> abstractValue = valuesMap.get(name);
|
||||||
|
if (abstractValue == null) {
|
||||||
|
throw new IllegalArgumentException("No value defined for " + name);
|
||||||
|
}
|
||||||
|
return abstractValue.hasValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the values (not the AbstractValues objects, but the T values of each AbstractValue)
|
||||||
|
* from the given map into this map. The given map must have exactly the same name and
|
||||||
|
* AbstractValue types as this map.
|
||||||
|
* @param otherMap The GValuesMap to copy values from
|
||||||
|
* @throws IllegalArgumentException if the given map does not have exactly the same set of
|
||||||
|
* names and types as this this map
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void copyValues(GValuesMap otherMap) {
|
||||||
|
for (AbstractValue<?> v : valuesMap.values()) {
|
||||||
|
AbstractValue<?> otherValue = otherMap.getAbstractValue(v.getName());
|
||||||
|
if (otherValue == null || otherValue.getClass() != v.getClass()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Can't copy values from incompatable " + getClass().getSimpleName() + "s!");
|
||||||
|
}
|
||||||
|
v.copyValue(v.getClass().cast(otherValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Define Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Boolean.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the default value for this boolean value.
|
||||||
|
* @return the new BooleanValue that was defined.
|
||||||
|
*/
|
||||||
|
public BooleanValue defineBoolean(String name, boolean defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
BooleanValue value = new BooleanValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type String, but with a restricted set of valid string values.
|
||||||
|
* @param name the name for this value.
|
||||||
|
* @param defaultValue an optional (can be null) initial value
|
||||||
|
* @param choices varargs list of valid string choices
|
||||||
|
* @return the new ChoiceValue that was defined
|
||||||
|
*/
|
||||||
|
public ChoiceValue defineChoice(String name, String defaultValue, String... choices) {
|
||||||
|
checkDup(name);
|
||||||
|
ChoiceValue value = new ChoiceValue(name, defaultValue, choices);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type File, but is restricted to directories.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue an optional initial value
|
||||||
|
* @return the new FileValue that was defined
|
||||||
|
*/
|
||||||
|
public FileValue defineDirectory(String name, File defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
FileValue value =
|
||||||
|
new FileValue(name, defaultValue, null, GhidraFileChooserMode.DIRECTORIES_ONLY);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Double with no initial default value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new DoubleValue that was defined
|
||||||
|
*/
|
||||||
|
public DoubleValue defineDouble(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
DoubleValue value = new DoubleValue(name, null);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Double with an initial value
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new DoubleValue that was defined
|
||||||
|
*/
|
||||||
|
public DoubleValue defineDouble(String name, double defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
DoubleValue value = new DoubleValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type File
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue an optional initial value
|
||||||
|
* @return the new FileValue that was defined
|
||||||
|
*/
|
||||||
|
public FileValue defineFile(String name, File defaultValue) {
|
||||||
|
return defineFile(name, defaultValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type File
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue an optional initial value
|
||||||
|
* @param startingDir specifies the starting directory when the FileChooser is invoked
|
||||||
|
* @return the new FileValue that was defined
|
||||||
|
*/
|
||||||
|
public FileValue defineFile(String name, File defaultValue, File startingDir) {
|
||||||
|
checkDup(name);
|
||||||
|
FileValue value =
|
||||||
|
new FileValue(name, defaultValue, startingDir, GhidraFileChooserMode.FILES_ONLY);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Integer that displays as a hex value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new IntValue that was defined
|
||||||
|
*/
|
||||||
|
public IntValue defineHexInt(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
IntValue value = new IntValue(name, null, true);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Integer with an initial value and displays as a hex value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new IntValue that was defined
|
||||||
|
*/
|
||||||
|
public IntValue defineHexInt(String name, int defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
IntValue value = new IntValue(name, defaultValue, true);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Long that displays as a hex value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new LongValue that was defined
|
||||||
|
*/
|
||||||
|
public LongValue defineHexLong(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
LongValue value = new LongValue(name, null, true);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Long with an initial value and displays as a hex value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new LongValue that was defined
|
||||||
|
*/
|
||||||
|
public LongValue defineHexLong(String name, long defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
LongValue value = new LongValue(name, defaultValue, true);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Integer with no initial value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new IntValue that was defined
|
||||||
|
*/
|
||||||
|
public IntValue defineInt(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
IntValue value = new IntValue(name, null, false);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Integer with an initial value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new IntValue that was defined
|
||||||
|
*/
|
||||||
|
public IntValue defineInt(String name, int defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
IntValue value = new IntValue(name, defaultValue, false);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Long with an initial value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new LongValue that was defined
|
||||||
|
*/
|
||||||
|
public LongValue defineLong(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
LongValue value = new LongValue(name, null, false);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type Long with an initial value.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value
|
||||||
|
* @return the new LongValue that was defined
|
||||||
|
*/
|
||||||
|
public LongValue defineLong(String name, long defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
LongValue value = new LongValue(name, defaultValue, false);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type String.
|
||||||
|
* @param name the name for this value
|
||||||
|
* @return the new StringValue that was defined
|
||||||
|
*/
|
||||||
|
public StringValue defineString(String name) {
|
||||||
|
checkDup(name);
|
||||||
|
StringValue value = new StringValue(name, null);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a value of type String with an optional initial value
|
||||||
|
* @param name the name for this value
|
||||||
|
* @param defaultValue the initial value (can be null)
|
||||||
|
* @return the new StringValue that was defined
|
||||||
|
*/
|
||||||
|
public StringValue defineString(String name, String defaultValue) {
|
||||||
|
checkDup(name);
|
||||||
|
StringValue value = new StringValue(name, defaultValue);
|
||||||
|
valuesMap.put(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Get Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the boolean value for the given name.
|
||||||
|
* @param name the name of a previously defined boolean value
|
||||||
|
* @return the boolean value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a boolean type
|
||||||
|
*/
|
||||||
|
public boolean getBoolean(String name) {
|
||||||
|
BooleanValue booleanValue = getValue(name, BooleanValue.class, "Boolean");
|
||||||
|
Boolean value = booleanValue.getValue();
|
||||||
|
return value == null ? false : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Choice (String) value for the given name. The value will be either null or one of
|
||||||
|
* the strings that were defined as valid choices.
|
||||||
|
* @param name the name of a previously defined Choice value
|
||||||
|
* @return the Choice value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a Choice type
|
||||||
|
*/
|
||||||
|
public String getChoice(String name) {
|
||||||
|
ChoiceValue choiceValue = getValue(name, ChoiceValue.class, "Choice");
|
||||||
|
return choiceValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the double value for the given name.
|
||||||
|
* @param name the name of a previously defined double value
|
||||||
|
* @return the double value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a double type
|
||||||
|
*/
|
||||||
|
public double getDouble(String name) {
|
||||||
|
DoubleValue doubleValue = getValue(name, DoubleValue.class, "Double");
|
||||||
|
Double value = doubleValue.getValue();
|
||||||
|
return value == null ? 0.0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link File} value for the given name.
|
||||||
|
* @param name the name of a previously defined File value
|
||||||
|
* @return the File value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a File type
|
||||||
|
*/
|
||||||
|
public File getFile(String name) {
|
||||||
|
FileValue fileValue = getValue(name, FileValue.class, "File");
|
||||||
|
return fileValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the int value for the given name.
|
||||||
|
* @param name the name of a previously defined int value
|
||||||
|
* @return the int value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a int type
|
||||||
|
*/
|
||||||
|
public int getInt(String name) {
|
||||||
|
IntValue intValue = getValue(name, IntValue.class, "Int");
|
||||||
|
Integer value = intValue.getValue();
|
||||||
|
return value == null ? 0 : value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the long value for the given name.
|
||||||
|
* @param name the name of a previously defined long value
|
||||||
|
* @return the long value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a long type
|
||||||
|
*/
|
||||||
|
public long getLong(String name) {
|
||||||
|
LongValue longValue = getValue(name, LongValue.class, "Int");
|
||||||
|
Long value = longValue.getValue();
|
||||||
|
return value == null ? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the String value for the given name.
|
||||||
|
* @param name the name of a previously defined String value
|
||||||
|
* @return the String value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a String type
|
||||||
|
*/
|
||||||
|
public String getString(String name) {
|
||||||
|
StringValue stringValue = getValue(name, StringValue.class, "String");
|
||||||
|
return stringValue.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Set Value Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the boolean value for the given name.
|
||||||
|
* @param name the name of the boolean value that was previously defined
|
||||||
|
* @param value the boolean to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a boolean type
|
||||||
|
*/
|
||||||
|
public void setBoolean(String name, boolean value) {
|
||||||
|
BooleanValue booleanValue = getValue(name, BooleanValue.class, "Boolean");
|
||||||
|
booleanValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Choice (String) value for the given name.
|
||||||
|
* @param name the name of the Choice value that was previously defined
|
||||||
|
* @param choice the string to set as the value. This String must be one of the defined choices
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a choice type
|
||||||
|
*/
|
||||||
|
public void setChoice(String name, String choice) {
|
||||||
|
ChoiceValue choiceValue = getValue(name, ChoiceValue.class, "Choice");
|
||||||
|
choiceValue.setValue(choice);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the double value for the given name.
|
||||||
|
* @param name the name of the double value that was previously defined
|
||||||
|
* @param value the double to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a double type
|
||||||
|
*/
|
||||||
|
public void setDouble(String name, double value) {
|
||||||
|
DoubleValue doubleValue = getValue(name, DoubleValue.class, "Double");
|
||||||
|
doubleValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link File} value for the given name.
|
||||||
|
* @param name the name of the File value that was previously defined
|
||||||
|
* @param value the File to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a File type
|
||||||
|
*/
|
||||||
|
public void setFile(String name, File value) {
|
||||||
|
FileValue fileValue = getValue(name, FileValue.class, "File");
|
||||||
|
fileValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the int value for the given name.
|
||||||
|
* @param name the name of the int value that was previously defined
|
||||||
|
* @param value the int to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a int type
|
||||||
|
*/
|
||||||
|
public void setInt(String name, int value) {
|
||||||
|
IntValue intValue = getValue(name, IntValue.class, "Int");
|
||||||
|
intValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the long value for the given name.
|
||||||
|
* @param name the name of the long value that was previously defined
|
||||||
|
* @param value the long to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a long type
|
||||||
|
*/
|
||||||
|
public void setLong(String name, long value) {
|
||||||
|
LongValue intValue = getValue(name, LongValue.class, "Long");
|
||||||
|
intValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the String value for the given name.
|
||||||
|
* @param name the name of the String value that was previously defined
|
||||||
|
* @param value the String to set as the value
|
||||||
|
* @throws IllegalArgumentException if the name hasn't been defined as a String type
|
||||||
|
*/
|
||||||
|
public void setString(String name, String value) {
|
||||||
|
StringValue stringValue = getValue(name, StringValue.class, "String");
|
||||||
|
stringValue.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Protected Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected <T> T getValue(String name, Class<T> c, String typeName) {
|
||||||
|
AbstractValue<?> value = valuesMap.get(name);
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException("No value defined for " + name);
|
||||||
|
}
|
||||||
|
if (value.getClass().isAssignableFrom(c)) {
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Wrong type! No " + typeName + " value defined for: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkDup(String name) {
|
||||||
|
if (valuesMap.containsKey(name)) {
|
||||||
|
throw new IllegalArgumentException("value already exits named " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link Integer} Value with an option for display the value as decimal or hex. The
|
||||||
|
* editor component uses an {@link IntegerTextField} for display and editing the value. This
|
||||||
|
* value supports the concept of no value which is represented by the text field being empty. If
|
||||||
|
* the text field is not empty, then the field only allows valid numeric values.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class IntValue extends AbstractValue<Integer> {
|
||||||
|
private boolean displayAsHex;
|
||||||
|
private IntegerTextField field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an IntValue that displays it value in decimal
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
|
public IntValue(String name) {
|
||||||
|
this(name, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an IntValue with a default value that displays it value in decimal
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the default value
|
||||||
|
*/
|
||||||
|
public IntValue(String name, int defaultValue) {
|
||||||
|
this(name, defaultValue, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an IntValue with a default value.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValue the default value
|
||||||
|
* @param displayAsHex if true, the value will be displayed as hex, otherwise it will display
|
||||||
|
* as decimal.
|
||||||
|
*/
|
||||||
|
public IntValue(String name, Integer defaultValue, boolean displayAsHex) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.displayAsHex = displayAsHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (field == null) {
|
||||||
|
field = new IntegerTextField(20);
|
||||||
|
field.setAllowsHexPrefix(false);
|
||||||
|
field.setShowNumberMode(false);
|
||||||
|
if (displayAsHex) {
|
||||||
|
field.setHexMode();
|
||||||
|
field.setShowNumberMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field.getComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
String text = field.getText();
|
||||||
|
|
||||||
|
// special case where user didn't enter a value on a string field that was defined without
|
||||||
|
// a value
|
||||||
|
if (getValue() == null && text.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(field.getIntValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
Integer value = getValue();
|
||||||
|
if (value == null) {
|
||||||
|
field.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer fromString(String valueString) {
|
||||||
|
return displayAsHex ? Integer.parseInt(valueString, 16) : Integer.parseInt(valueString, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Integer v) {
|
||||||
|
return displayAsHex ? Integer.toHexString(v) : v.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for Long Values with an option for display the value as decimal or hex. The
|
||||||
|
* editor component uses an {@link IntegerTextField} for display and editing the value. This
|
||||||
|
* value supports the concept of no value which is represented by the text field being empty. If
|
||||||
|
* the text field is not empty, then the field only allows valid numeric values.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class LongValue extends AbstractValue<Long> {
|
||||||
|
private boolean displayAsHex;
|
||||||
|
private IntegerTextField field;
|
||||||
|
|
||||||
|
public LongValue(String name) {
|
||||||
|
this(name, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongValue(String name, Long defaultValue) {
|
||||||
|
this(name, defaultValue, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongValue(String name, boolean displayAsHex) {
|
||||||
|
this(name, null, displayAsHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongValue(String name, Long defaultValue, boolean displayAsHex) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
this.displayAsHex = displayAsHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (field == null) {
|
||||||
|
field = new IntegerTextField(20);
|
||||||
|
field.setAllowsHexPrefix(false);
|
||||||
|
field.setShowNumberMode(false);
|
||||||
|
if (displayAsHex) {
|
||||||
|
field.setHexMode();
|
||||||
|
field.setShowNumberMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field.getComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
String text = field.getText();
|
||||||
|
|
||||||
|
// special case where user didn't enter a value on a string field that was defined without
|
||||||
|
// a value
|
||||||
|
if (getValue() == null && text.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(field.getLongValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
Long value = getValue();
|
||||||
|
if (value == null) {
|
||||||
|
field.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long fromString(String valueString) {
|
||||||
|
return displayAsHex ? Long.parseLong(valueString, 16) : Long.parseLong(valueString, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsText() {
|
||||||
|
Long v = getValue();
|
||||||
|
if (v == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return displayAsHex ? Long.toHexString(v) : Long.toString(v);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value class for {@link String} values.
|
||||||
|
* <P>
|
||||||
|
* This class and other subclasses of {@link AbstractValue} are part of a subsystem for easily
|
||||||
|
* defining a set of values that can be displayed in an input dialog ({@link ValuesMapDialog}).
|
||||||
|
* Typically, these values are created indirectly using a {@link GValuesMap} which is then
|
||||||
|
* given to the constructor of the dialog. However, an alternate approach is to create the
|
||||||
|
* dialog without a ValuesMap and then use its {@link ValuesMapDialog#addValue(AbstractValue)}
|
||||||
|
* method directly.
|
||||||
|
*/
|
||||||
|
public class StringValue extends AbstractValue<String> {
|
||||||
|
private JTextField textField;
|
||||||
|
|
||||||
|
public StringValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringValue(String name, String defaultValue) {
|
||||||
|
super(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getComponent() {
|
||||||
|
if (textField == null) {
|
||||||
|
textField = new JTextField(20);
|
||||||
|
}
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateValueFromComponent() {
|
||||||
|
String text = textField.getText();
|
||||||
|
|
||||||
|
// special case where user didn't enter a value on a string field that was defined without
|
||||||
|
// a value
|
||||||
|
if (getValue() == null && text.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateComponentFromValue() {
|
||||||
|
textField.setText(getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromString(String valueString) {
|
||||||
|
|
||||||
|
return valueString;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.widgets.label.GHtmlLabel;
|
||||||
|
import ghidra.util.HTMLUtilities;
|
||||||
|
import ghidra.util.MessageType;
|
||||||
|
import ghidra.util.layout.PairLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for displaying and editing values defined in a {@link GValuesMap}. The dialog consists
|
||||||
|
* of an option message, followed by a list of name / value pairs. The name / value pairs will
|
||||||
|
* be display in the order they were defined in the ValuesMap.
|
||||||
|
*/
|
||||||
|
public class ValuesMapDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
|
private static final int MAX_MESSAGE_LINE_WIDTH = 60;
|
||||||
|
private JPanel valuesPanel;
|
||||||
|
private GValuesMap valuesMap;
|
||||||
|
private boolean cancelled = false;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the dialog with the given title and optional message. The message will be display
|
||||||
|
* at the top of the dialog before the list of name / value pairs. This form of the dialog
|
||||||
|
* requires that the {@link #addValue(AbstractValue)} method be called to populate the
|
||||||
|
* ValuesMap.
|
||||||
|
* @param title the title for the dialog
|
||||||
|
* @param message the optional message to display before the list of name value pairs
|
||||||
|
*/
|
||||||
|
public ValuesMapDialog(String title, String message) {
|
||||||
|
this(title, message, new GValuesMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the dialog with the given title and optional message. The message will be display
|
||||||
|
* at the top of the dialog before the list of name / value pairs. The values are provided
|
||||||
|
* at construction time.
|
||||||
|
* @param title the title for the dialog
|
||||||
|
* @param message the optional message to display before the list of name value pairs
|
||||||
|
* @param valuesMap the ValuesMap whose values are to be displayed.
|
||||||
|
*/
|
||||||
|
public ValuesMapDialog(String title, String message, GValuesMap valuesMap) {
|
||||||
|
super(title);
|
||||||
|
this.message = message;
|
||||||
|
this.valuesMap = valuesMap;
|
||||||
|
|
||||||
|
valuesPanel = buildValuesPanel();
|
||||||
|
|
||||||
|
addWorkPanel(buildWorkPanel());
|
||||||
|
|
||||||
|
for (AbstractValue<?> value : valuesMap.getValues()) {
|
||||||
|
buildComponentsForValue(value);
|
||||||
|
}
|
||||||
|
setRememberSize(false);
|
||||||
|
|
||||||
|
addOKButton();
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new value to the ValuesMap being edited by this dialog.
|
||||||
|
* @param value the new AbstractValue to be added
|
||||||
|
* @return the value that was added
|
||||||
|
*/
|
||||||
|
public AbstractValue<?> addValue(AbstractValue<?> value) {
|
||||||
|
valuesMap.addValue(value);
|
||||||
|
buildComponentsForValue(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link ValuesMapValidator} on the ValuesMap being edited. This is usually set on the
|
||||||
|
* ValuesMap before the dialog is constructed. This method is for uses where it wasn't
|
||||||
|
* constructed with a ValueMap, but values were added directly to the dialog after dialog
|
||||||
|
* construction.
|
||||||
|
* @param validator the ValuesMapValidator
|
||||||
|
*/
|
||||||
|
public void setValidator(ValuesMapValidator validator) {
|
||||||
|
valuesMap.setValidator(validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ValuesMap being edited.
|
||||||
|
* @return the ValuesMap being edited.
|
||||||
|
*/
|
||||||
|
public GValuesMap getValues() {
|
||||||
|
if (cancelled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return valuesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the dialog was cancelled.
|
||||||
|
* @return true if the dialog was cancelled.
|
||||||
|
*/
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void okCallback() {
|
||||||
|
try {
|
||||||
|
valuesMap.updateFromComponents();
|
||||||
|
}
|
||||||
|
catch (ValuesMapParseException e) {
|
||||||
|
setStatusText(e.getMessage(), MessageType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (valuesMap.isValid(this)) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancelCallback() {
|
||||||
|
cancelled = true;
|
||||||
|
super.cancelCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel buildValuesPanel() {
|
||||||
|
JPanel panel = new JPanel(new PairLayout(4, 10));
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JComponent buildWorkPanel() {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
if (message != null) {
|
||||||
|
String literalHTML = HTMLUtilities.toLiteralHTML(message, MAX_MESSAGE_LINE_WIDTH);
|
||||||
|
GHtmlLabel label = new GHtmlLabel(literalHTML);
|
||||||
|
|
||||||
|
label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
panel.add(label, BorderLayout.NORTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
JScrollPane scroll = new JScrollPane(valuesPanel);
|
||||||
|
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
|
panel.add(scroll, BorderLayout.CENTER);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildComponentsForValue(AbstractValue<?> value) {
|
||||||
|
valuesPanel.add(new JLabel(value.getName() + ":", SwingConstants.RIGHT));
|
||||||
|
valuesPanel.add(value.getComponent());
|
||||||
|
value.updateComponentFromValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when processing/parsing ValuesMap values. Mostly exists so that the exception
|
||||||
|
* message is uniform throught the types.
|
||||||
|
*/
|
||||||
|
public class ValuesMapParseException extends Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param valueName the name of the value that was being processed
|
||||||
|
* @param type the type name of the value that was being processed
|
||||||
|
* @param message the detail message of what went wrong
|
||||||
|
*/
|
||||||
|
public ValuesMapParseException(String valueName, String type, String message) {
|
||||||
|
super("Error processing " + type + " value \"" + valueName + "\"! " + message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import ghidra.util.StatusListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for validating values in a {@link GValuesMap}
|
||||||
|
*/
|
||||||
|
public interface ValuesMapValidator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates one or more values in the given ValuesMap. This is used by the ValuesMapDialog
|
||||||
|
* to validate values when the user presses the "Ok" button. If it returns true, the dialog
|
||||||
|
* will close. Otherwise, the dialog will remain visible, displaying the error message that
|
||||||
|
* was reported to the given StatusListener.
|
||||||
|
* @param values the ValuesMap whose values are to be validated
|
||||||
|
* @param statusListener a {@link StatusListener} to report validation errors back to
|
||||||
|
* the dialog
|
||||||
|
* @return true if the values pass the validation check.
|
||||||
|
*/
|
||||||
|
public boolean validate(GValuesMap values, StatusListener statusListener);
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.DockingWindowManager;
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
|
||||||
|
public abstract class AbstractValueTest extends AbstractDockingTest {
|
||||||
|
|
||||||
|
protected ValuesMapDialog dialog;
|
||||||
|
protected GValuesMap values = new GValuesMap();
|
||||||
|
|
||||||
|
protected void showDialogOnSwingWithoutBlocking() {
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
dialog = new ValuesMapDialog("Test", null, values);
|
||||||
|
DockingWindowManager.showDialog(dialog);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
waitForDialogComponent(DialogComponentProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTextOnComponent(AbstractValue<?> nameValue, String text) {
|
||||||
|
runSwing(() -> {
|
||||||
|
JTextField field = (JTextField) nameValue.getComponent();
|
||||||
|
field.setText(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pressOk() {
|
||||||
|
JButton okButton = (JButton) getInstanceField("okButton", dialog);
|
||||||
|
runSwing(() -> okButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pressCancel() {
|
||||||
|
JButton okButton = (JButton) getInstanceField("cancelButton", dialog);
|
||||||
|
runSwing(() -> okButton.doClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import docking.widgets.values.BooleanValue;
|
||||||
|
|
||||||
|
public class BooleanValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "YesNo";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBooleanValue() {
|
||||||
|
values.defineBoolean(NAME, false);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(false, values.getBoolean(NAME));
|
||||||
|
|
||||||
|
values.setBoolean(NAME, true);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(true, values.getBoolean(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
BooleanValue value1 = new BooleanValue(NAME, true);
|
||||||
|
BooleanValue value2 = new BooleanValue(NAME, false);
|
||||||
|
|
||||||
|
assertEquals("true", value1.getAsText());
|
||||||
|
assertEquals("false", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
BooleanValue result = new BooleanValue(NAME, false);
|
||||||
|
|
||||||
|
assertTrue(result.setAsText("true"));
|
||||||
|
assertFalse(result.setAsText("false"));
|
||||||
|
assertTrue(result.setAsText("TRUE"));
|
||||||
|
assertFalse(result.setAsText("asdas"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValueWithNoDialogInput() {
|
||||||
|
values.defineBoolean(NAME, false);
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertEquals(false, values.getBoolean(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValueWithDialogInput() {
|
||||||
|
values.defineBoolean(NAME, false);
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setBoolean(values.getAbstractValue(NAME), true);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertEquals(true, values.getBoolean(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBoolean(AbstractValue<?> boolValue, boolean b) {
|
||||||
|
runSwing(() -> {
|
||||||
|
JCheckBox checkBox = (JCheckBox) boolValue.getComponent();
|
||||||
|
checkBox.setSelected(b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.combobox.GComboBox;
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import docking.widgets.values.ChoiceValue;
|
||||||
|
|
||||||
|
public class ChoiceValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "Choice";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChoiceValueNoDefault() {
|
||||||
|
values.defineChoice(NAME, null, "A", "B", "C");
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setChoice(NAME, "B");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals("B", values.getChoice(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChoiceValueWithDefault() {
|
||||||
|
values.defineChoice(NAME, "A", "A", "B", "C");
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("A", values.getChoice(NAME));
|
||||||
|
|
||||||
|
values.setChoice(NAME, "C");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals("C", values.getChoice(NAME));
|
||||||
|
|
||||||
|
values.setChoice(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
ChoiceValue v1 = new ChoiceValue(NAME, "A", "A", "B");
|
||||||
|
ChoiceValue v2 = new ChoiceValue(NAME, null, "A", "B");
|
||||||
|
|
||||||
|
assertEquals("A", v1.getAsText());
|
||||||
|
assertNull(v2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
ChoiceValue result = new ChoiceValue(NAME, null, "A", "B");
|
||||||
|
|
||||||
|
assertEquals("A", result.setAsText("A"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.setAsText("Z");
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChoiceWithInValidDefault() {
|
||||||
|
try {
|
||||||
|
values.defineChoice(NAME, "Z", "A", "B", "C");
|
||||||
|
fail("Was able to set bad default value in choice");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineChoice(NAME, null, "A", "B", "C");
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getChoice(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineChoice(NAME, null, "A", "B", "C");
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setCombo(values.getAbstractValue(NAME), "C");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("C", values.getChoice(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineChoice(NAME, "B", "A", "B", "C");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("B", values.getChoice(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineChoice(NAME, "C", "A", "B", "C");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setCombo(values.getAbstractValue(NAME), "A");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("A", values.getChoice(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCombo(AbstractValue<?> choiceValue, String choice) {
|
||||||
|
runSwing(() -> {
|
||||||
|
GComboBox<?> combo = (GComboBox<?>) choiceValue.getComponent();
|
||||||
|
combo.setSelectedItem(choice);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.DoubleValue;
|
||||||
|
|
||||||
|
public class DoubleValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "Fraction";
|
||||||
|
private static double DELTA = 0.0001;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoubleValueNoDefault() {
|
||||||
|
values.defineDouble(NAME);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getDouble(NAME), DELTA); // the getPrimitive returns 0 when value is null
|
||||||
|
|
||||||
|
values.setDouble(NAME, 6);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoubleValueWithDefault() {
|
||||||
|
values.defineDouble(NAME, 3.2);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(3.2, values.getDouble(NAME), DELTA);
|
||||||
|
|
||||||
|
values.setDouble(NAME, 6.5);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6.5, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
DoubleValue v1 = new DoubleValue(NAME, 1.23);
|
||||||
|
DoubleValue v2 = new DoubleValue(NAME);
|
||||||
|
assertEquals("1.23", v1.getAsText());
|
||||||
|
assertNull(v2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
DoubleValue v1 = new DoubleValue(NAME);
|
||||||
|
|
||||||
|
assertEquals((Double) 1.23, v1.setAsText("1.23"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineDouble(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineDouble(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "1.23");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(1.23, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineDouble(NAME, 1.2);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(1.2, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineDouble(NAME, 1.2);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "4.3");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(4.3, values.getDouble(NAME), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
import docking.widgets.values.AbstractValue;
|
||||||
|
import docking.widgets.values.FileValue;
|
||||||
|
import docking.widgets.values.FileValue.FileValuePanel;
|
||||||
|
|
||||||
|
public class FileValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "My File";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileValueNoDefault() {
|
||||||
|
values.defineFile(NAME, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, new File("."));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(new File("."), values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileValueWithDefault() {
|
||||||
|
values.defineFile(NAME, new File("/"));
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(new File("/"), values.getFile(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, new File("."));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(new File("."), values.getFile(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
FileValue value1 = new FileValue(NAME);
|
||||||
|
FileValue value2 = new FileValue(NAME, new File("/"));
|
||||||
|
assertNull(value1.getAsText());
|
||||||
|
assertEquals("/", value2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
FileValue v = new FileValue(NAME);
|
||||||
|
assertEquals(new File("/abc"), v.setAsText("/abc"));
|
||||||
|
try {
|
||||||
|
v.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectoryValueNoDefault() {
|
||||||
|
values.defineDirectory(NAME, null);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, new File("."));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(new File("."), values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectoryValueWithDefault() {
|
||||||
|
values.defineDirectory(NAME, new File("/"));
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(new File("/"), values.getFile(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, new File("."));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(new File("."), values.getFile(NAME));
|
||||||
|
|
||||||
|
values.setFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() throws IOException {
|
||||||
|
File foo = createTempFile("foo");
|
||||||
|
values.defineFile(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setFile(values.getAbstractValue(NAME), foo);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(foo, values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineFile(NAME, new File("/"));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(new File("/"), values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() throws IOException {
|
||||||
|
File foo = createTempFile("foo");
|
||||||
|
File bar = createTempFile("bar");
|
||||||
|
values.defineFile(NAME, foo);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setFile(values.getAbstractValue(NAME), bar);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(bar, values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectoryWithDialogInput() throws IOException {
|
||||||
|
File dir = createTempDirectory("foo");
|
||||||
|
values.defineDirectory(NAME, null);
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setFile(values.getAbstractValue(NAME), dir);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertEquals(dir, values.getFile(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartingDir() throws IOException {
|
||||||
|
File file = createTempDirectory("foo");
|
||||||
|
File parent = file.getParentFile();
|
||||||
|
values.defineFile(NAME, null, parent);
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
FileValuePanel fileWidget = (FileValuePanel) values.getAbstractValue(NAME).getComponent();
|
||||||
|
pressButtonByName(fileWidget, "BrowseButton", false);
|
||||||
|
GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
|
||||||
|
File dir = runSwing(() -> chooser.getCurrentDirectory());
|
||||||
|
pressButtonByText(chooser, "Cancel");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertEquals(parent, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setFile(AbstractValue<?> nameValue, File f) {
|
||||||
|
FileValuePanel fileWidget = (FileValuePanel) nameValue.getComponent();
|
||||||
|
pressButtonByName(fileWidget, "BrowseButton", false);
|
||||||
|
GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
|
||||||
|
runSwing(() -> {
|
||||||
|
chooser.setSelectedFile(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
pressButtonByText(chooser, "OK");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.IntValue;
|
||||||
|
|
||||||
|
public class IntValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "Count";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntValueNoDefault() {
|
||||||
|
values.defineInt(NAME);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getInt(NAME)); // the getPrimitive returns 0 when value is null
|
||||||
|
|
||||||
|
values.setInt(NAME, 6);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntValueWithDefault() {
|
||||||
|
values.defineInt(NAME, 32);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(32, values.getInt(NAME));
|
||||||
|
|
||||||
|
values.setInt(NAME, 6);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
IntValue v1 = new IntValue(NAME, 12);
|
||||||
|
IntValue v2 = new IntValue(NAME);
|
||||||
|
IntValue v3 = new IntValue(NAME, 10, true /*displayAsHex*/);
|
||||||
|
assertEquals("12", v1.getAsText());
|
||||||
|
assertNull(v2.getAsText());
|
||||||
|
assertEquals("a", v3.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
IntValue v1 = new IntValue(NAME);
|
||||||
|
IntValue v2 = new IntValue(NAME, null, true /*displayAsText*/);
|
||||||
|
|
||||||
|
assertEquals((Integer) 10, v1.setAsText("10"));
|
||||||
|
assertEquals((Integer) 16, v2.setAsText("10"));
|
||||||
|
assertEquals((Integer) 10, v2.setAsText("A"));
|
||||||
|
assertEquals((Integer) 10, v2.setAsText("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineInt(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineInt(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "123");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(123, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineInt(NAME, 12);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(12, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineInt(NAME, 12);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "43");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(43, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHexMode() {
|
||||||
|
values.defineHexInt(NAME, 12);
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "A");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(10, values.getInt(NAME));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.LongValue;
|
||||||
|
|
||||||
|
public class LongValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "Count";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testlongValueNoDefault() {
|
||||||
|
values.defineLong(NAME);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getLong(NAME)); // the getPrimitive returns 0 when value is null
|
||||||
|
|
||||||
|
values.setLong(NAME, 6);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testlongValueWithDefault() {
|
||||||
|
values.defineLong(NAME, 32);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(32, values.getLong(NAME));
|
||||||
|
|
||||||
|
values.setLong(NAME, 6);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals(6, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
LongValue v1 = new LongValue(NAME, 12L);
|
||||||
|
LongValue v2 = new LongValue(NAME);
|
||||||
|
LongValue v3 = new LongValue(NAME, 10L, true /*displayAsHex*/);
|
||||||
|
assertEquals("12", v1.getAsText());
|
||||||
|
assertNull(v2.getAsText());
|
||||||
|
assertEquals("a", v3.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
LongValue v1 = new LongValue(NAME);
|
||||||
|
LongValue v2 = new LongValue(NAME, null, true /*displayAsText*/);
|
||||||
|
|
||||||
|
assertEquals((Long) 10L, v1.setAsText("10"));
|
||||||
|
assertEquals((Long) 16L, v2.setAsText("10"));
|
||||||
|
assertEquals((Long) 10L, v2.setAsText("A"));
|
||||||
|
assertEquals((Long) 10L, v2.setAsText("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineLong(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertEquals(0, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineLong(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "123");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(123, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineLong(NAME, 12);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(12, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineLong(NAME, 12);
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "43");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(43, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHexMode() {
|
||||||
|
values.defineHexLong(NAME, 12);
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "A");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals(10, values.getLong(NAME));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.widgets.values.StringValue;
|
||||||
|
|
||||||
|
public class StringValueTest extends AbstractValueTest {
|
||||||
|
private static final String NAME = "Name";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStringValueNoDefault() {
|
||||||
|
values.defineString(NAME);
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setString(NAME, "abc");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals("abc", values.getString(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStringValueWithDefault() {
|
||||||
|
values.defineString(NAME, "ABC");
|
||||||
|
|
||||||
|
assertTrue(values.isDefined(NAME));
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
values.setString(NAME, "xyz");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
assertEquals("xyz", values.getString(NAME));
|
||||||
|
|
||||||
|
values.setString(NAME, null);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAsText() {
|
||||||
|
StringValue v1 = new StringValue(NAME, "A");
|
||||||
|
StringValue v2 = new StringValue(NAME);
|
||||||
|
|
||||||
|
assertEquals("A", v1.getAsText());
|
||||||
|
assertNull(v2.getAsText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAsText() {
|
||||||
|
StringValue result = new StringValue(NAME);
|
||||||
|
|
||||||
|
assertEquals("A", result.setAsText("A"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.setAsText(null);
|
||||||
|
fail("Expected exception");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineString(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
assertNull(values.getString(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDefaultValueWithDialogInput() {
|
||||||
|
values.defineString(NAME);
|
||||||
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "xyz");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("xyz", values.getString(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
|
values.defineString(NAME, "abc");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("abc", values.getString(NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultValueWithDialogInput() {
|
||||||
|
values.defineString(NAME, "abc");
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(values.getAbstractValue(NAME), "xyz");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
assertTrue(values.hasValue(NAME));
|
||||||
|
assertEquals("xyz", values.getString(NAME));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* ###
|
||||||
|
* 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.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ValuesMapDialogTest extends AbstractValueTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetValueAsWrongType() {
|
||||||
|
AbstractValue<Long> ageValue = values.defineLong("Age");
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(ageValue, "42");
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertEquals(42, values.getInt("Age"));
|
||||||
|
fail("Should not be able to retrieve a value with the wrong type!");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCancelDoesntChangeValue() {
|
||||||
|
AbstractValue<String> nameValue = values.defineString("Name", "abc");
|
||||||
|
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setTextOnComponent(nameValue, "Joe");
|
||||||
|
pressCancel();
|
||||||
|
|
||||||
|
assertTrue(dialog.isCancelled());
|
||||||
|
assertEquals("abc", values.getString("Name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue