mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-3992 fixed various issues/bugs related to the new askValues() script method
This commit is contained in:
parent
134806cbe4
commit
9cd2666799
22 changed files with 556 additions and 383 deletions
|
@ -17,7 +17,6 @@
|
||||||
// @category Examples
|
// @category Examples
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.features.base.values.GhidraValuesMap;
|
import ghidra.features.base.values.GhidraValuesMap;
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.MessageType;
|
import ghidra.util.MessageType;
|
||||||
|
|
||||||
|
@ -28,20 +27,12 @@ public class AskValuesExampleScript extends GhidraScript {
|
||||||
GhidraValuesMap values = new GhidraValuesMap();
|
GhidraValuesMap values = new GhidraValuesMap();
|
||||||
|
|
||||||
values.defineString("Name");
|
values.defineString("Name");
|
||||||
values.defineAddress("Address", currentProgram);
|
|
||||||
values.defineInt("Count");
|
values.defineInt("Count");
|
||||||
values.defineInt("Max Results", 100);
|
values.defineInt("Max Results", 100);
|
||||||
values.defineChoice("Priority", "Low", "Low", "Medium", "High");
|
values.defineChoice("Priority", "Low", "Low", "Medium", "High");
|
||||||
|
values.defineProgram("Other Program");
|
||||||
// When asking for a program, you must supply a consumer that you will use
|
values.defineProjectFile("Project File");
|
||||||
// to release the program. Since programs share open instances, Ghidra uses
|
values.defineProjectFolder("Project Folder");
|
||||||
// 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
|
// 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.
|
// is called when the "Ok" button is pushed and must return true before the dialog exits.
|
||||||
|
@ -70,14 +61,23 @@ public class AskValuesExampleScript extends GhidraScript {
|
||||||
//dialog. The values map returned may or may not be the same instance as the one passed in.
|
//dialog. The values map returned may or may not be the same instance as the one passed in.
|
||||||
|
|
||||||
String name = values.getString("Name");
|
String name = values.getString("Name");
|
||||||
Address address = values.getAddress("Address");
|
|
||||||
int age = values.getInt("Count");
|
int age = values.getInt("Count");
|
||||||
int max = values.getInt("Max Results");
|
int max = values.getInt("Max Results");
|
||||||
String priority = values.getChoice("Priority");
|
String priority = values.getChoice("Priority");
|
||||||
Program program = values.getProgram("Other Program");
|
|
||||||
|
// 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 it. 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.
|
||||||
|
// NOTE: if you call getProgram() more than once, the consumer will be added multiple times
|
||||||
|
// and you must release it multiple times
|
||||||
|
Program program = values.getProgram("Other Program", this, state.getTool());
|
||||||
|
|
||||||
println("Name = " + name);
|
println("Name = " + name);
|
||||||
println("Address = " + address);
|
|
||||||
println("Count = " + age);
|
println("Count = " + age);
|
||||||
println("Max Results = " + max);
|
println("Max Results = " + max);
|
||||||
println("Priority = " + priority);
|
println("Priority = " + priority);
|
||||||
|
|
|
@ -2360,6 +2360,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
|
|
||||||
public GhidraValuesMap askValues(String title, String optionalMessage, GhidraValuesMap values)
|
public GhidraValuesMap askValues(String title, String optionalMessage, GhidraValuesMap values)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
values.setTaskMonitor(monitor);
|
||||||
for (AbstractValue<?> value : values.getValues()) {
|
for (AbstractValue<?> value : values.getValues()) {
|
||||||
String key = join(title, value.getName());
|
String key = join(title, value.getName());
|
||||||
loadAskValue(value.getValue(), s -> value.setAsText(s), key);
|
loadAskValue(value.getValue(), s -> value.setAsText(s), key);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.framework.*;
|
||||||
import ghidra.framework.client.ClientUtil;
|
import ghidra.framework.client.ClientUtil;
|
||||||
import ghidra.framework.client.RepositoryAdapter;
|
import ghidra.framework.client.RepositoryAdapter;
|
||||||
import ghidra.framework.data.*;
|
import ghidra.framework.data.*;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.project.DefaultProject;
|
import ghidra.framework.project.DefaultProject;
|
||||||
import ghidra.framework.project.DefaultProjectManager;
|
import ghidra.framework.project.DefaultProjectManager;
|
||||||
|
@ -424,6 +425,7 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
if (locator.getProjectDir().exists()) {
|
if (locator.getProjectDir().exists()) {
|
||||||
project = openProject(locator);
|
project = openProject(locator);
|
||||||
|
AppInfo.setActiveProject(project);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (options.runScriptsNoImport) {
|
if (options.runScriptsNoImport) {
|
||||||
|
@ -440,6 +442,7 @@ public class HeadlessAnalyzer {
|
||||||
Msg.info(this, "Creating " + (options.deleteProject ? "temporary " : "") +
|
Msg.info(this, "Creating " + (options.deleteProject ? "temporary " : "") +
|
||||||
"project: " + locator);
|
"project: " + locator);
|
||||||
project = getProjectManager().createProject(locator, null, false);
|
project = getProjectManager().createProject(locator, null, false);
|
||||||
|
AppInfo.setActiveProject(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -457,6 +460,7 @@ public class HeadlessAnalyzer {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
project.close();
|
project.close();
|
||||||
|
AppInfo.setActiveProject(null);
|
||||||
if (!options.runScriptsNoImport && options.deleteProject) {
|
if (!options.runScriptsNoImport && options.deleteProject) {
|
||||||
FileUtilities.deleteDir(locator.getProjectDir());
|
FileUtilities.deleteDir(locator.getProjectDir());
|
||||||
locator.getMarkerFile().delete();
|
locator.getMarkerFile().delete();
|
||||||
|
@ -1546,8 +1550,7 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
// Analyze the primary program, and determine if we should save.
|
// Analyze the primary program, and determine if we should save.
|
||||||
// TODO: Analyze non-primary programs (GP-2965).
|
// TODO: Analyze non-primary programs (GP-2965).
|
||||||
boolean doSave =
|
boolean doSave = analyzeProgram(fsrl.toString(), primaryProgram) && !options.readOnly;
|
||||||
analyzeProgram(fsrl.toString(), primaryProgram) && !options.readOnly;
|
|
||||||
|
|
||||||
// The act of marking the program as temporary by a script will signal
|
// The act of marking the program as temporary by a script will signal
|
||||||
// us to discard any changes
|
// us to discard any changes
|
||||||
|
@ -1706,7 +1709,7 @@ public class HeadlessAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processAsFS(FSRL fsrl, String folderPath, int depth) throws CancelledException {
|
private boolean processAsFS(FSRL fsrl, String folderPath, int depth) throws CancelledException {
|
||||||
try (FileSystemRef fsRef = fsService.probeFileForFilesystem(fsrl, TaskMonitor.DUMMY,
|
try (FileSystemRef fsRef = fsService.probeFileForFilesystem(fsrl, TaskMonitor.DUMMY,
|
||||||
FileSystemProbeConflictResolver.CHOOSEFIRST)) {
|
FileSystemProbeConflictResolver.CHOOSEFIRST)) {
|
||||||
|
@ -1746,7 +1749,6 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
storage.clear();
|
storage.clear();
|
||||||
|
|
||||||
|
|
||||||
if (inputDirFiles != null && !inputDirFiles.isEmpty()) {
|
if (inputDirFiles != null && !inputDirFiles.isEmpty()) {
|
||||||
Msg.info(this, "REPORT: Processing input files: ");
|
Msg.info(this, "REPORT: Processing input files: ");
|
||||||
Msg.info(this, " project: " + project.getProjectLocator());
|
Msg.info(this, " project: " + project.getProjectLocator());
|
||||||
|
|
|
@ -16,102 +16,91 @@
|
||||||
package ghidra.features.base.values;
|
package ghidra.features.base.values;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.widgets.button.BrowseButton;
|
import docking.widgets.button.BrowseButton;
|
||||||
import ghidra.framework.main.AppInfo;
|
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.store.FileSystem;
|
import ghidra.framework.store.FileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used by Values that use the DataTreeDialog for picking DomainFiles and DomainFolders
|
* Base class for either project file chooser or project folder chooser
|
||||||
*/
|
*/
|
||||||
class ProjectBrowserPanel extends JPanel {
|
abstract class AbstractProjectBrowserPanel extends JPanel {
|
||||||
private JTextField textField;
|
protected Project project;
|
||||||
|
protected JTextField textField;
|
||||||
private JButton browseButton;
|
private JButton browseButton;
|
||||||
private boolean selectFolders;
|
private DomainFolder startFolder;
|
||||||
|
private int type;
|
||||||
|
protected DomainFileFilter filter = null;
|
||||||
|
|
||||||
ProjectBrowserPanel(String name, boolean selectFolders) {
|
AbstractProjectBrowserPanel(int type, Project project, String name, String startPath) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.selectFolders = selectFolders;
|
this.type = type;
|
||||||
|
this.project = Objects.requireNonNull(project);
|
||||||
|
this.startFolder = parseDomainFolder(project, startPath);
|
||||||
setName(name);
|
setName(name);
|
||||||
textField = new JTextField(20);
|
textField = new JTextField(20);
|
||||||
browseButton = new BrowseButton();
|
browseButton = new BrowseButton();
|
||||||
browseButton.addActionListener(e -> showDomainFileChooser());
|
browseButton.addActionListener(e -> showProjectChooser());
|
||||||
add(textField, BorderLayout.CENTER);
|
add(textField, BorderLayout.CENTER);
|
||||||
add(browseButton, BorderLayout.EAST);
|
add(browseButton, BorderLayout.EAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDomainFile(DomainFile value) {
|
private void showProjectChooser() {
|
||||||
String text = value == null ? "" : value.getPathname();
|
DataTreeDialog dialog =
|
||||||
textField.setText(text);
|
new DataTreeDialog(null, "Choose " + getName(), type, filter, project);
|
||||||
}
|
|
||||||
|
|
||||||
void setDomainFolder(DomainFolder value) {
|
if (startFolder != null) {
|
||||||
String text = value == null ? "" : value.getPathname();
|
dialog.selectFolder(startFolder);
|
||||||
textField.setText(text);
|
}
|
||||||
}
|
intializeCurrentValue(dialog);
|
||||||
|
|
||||||
private void showDomainFileChooser() {
|
|
||||||
DataTreeDialog dialog = new DataTreeDialog(null, "Choose " + getName(),
|
|
||||||
selectFolders ? DataTreeDialog.CHOOSE_FOLDER : DataTreeDialog.OPEN);
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
if (dialog.wasCancelled()) {
|
if (dialog.wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String text = selectFolders ? dialog.getDomainFolder().getPathname()
|
String text = getSelectedPath(dialog);
|
||||||
: dialog.getDomainFile().getPathname();
|
|
||||||
textField.setText(text);
|
textField.setText(text);
|
||||||
dialog.dispose();
|
dialog.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainFile getDomainFile() {
|
protected abstract String getSelectedPath(DataTreeDialog dialog);
|
||||||
String text = textField.getText().trim();
|
|
||||||
if (text.isBlank()) {
|
protected abstract void intializeCurrentValue(DataTreeDialog dialog);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return parseDomainFile(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getText() {
|
String getText() {
|
||||||
return textField.getText().trim();
|
return textField.getText().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainFolder getDomainFolder() {
|
static DomainFile parseDomainFile(Project project, String val) {
|
||||||
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
|
// Add the slash to make it an absolute path
|
||||||
if (!val.isEmpty() && val.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
if (!val.isEmpty() && val.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
val = FileSystem.SEPARATOR_CHAR + val;
|
val = FileSystem.SEPARATOR_CHAR + val;
|
||||||
}
|
}
|
||||||
Project activeProject = AppInfo.getActiveProject();
|
DomainFile df = project.getProjectData().getFile(val);
|
||||||
DomainFile df = activeProject.getProjectData().getFile(val);
|
|
||||||
if (df != null) {
|
if (df != null) {
|
||||||
return df;
|
return df;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DomainFolder parseDomainFolder(String path) {
|
static DomainFolder parseDomainFolder(Project project, String path) {
|
||||||
|
if (path == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
path = path.trim();
|
path = path.trim();
|
||||||
// Add the slash to make it an absolute path
|
// Add the slash to make it an absolute path
|
||||||
if (path.isEmpty() || path.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
if (path.isEmpty() || path.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
path = FileSystem.SEPARATOR_CHAR + path;
|
path = FileSystem.SEPARATOR_CHAR + path;
|
||||||
}
|
}
|
||||||
Project activeProject = AppInfo.getActiveProject();
|
DomainFolder df = project.getProjectData().getFolder(path);
|
||||||
DomainFolder df = activeProject.getProjectData().getFolder(path);
|
|
||||||
if (df != null) {
|
if (df != null) {
|
||||||
return df;
|
return df;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.features.base.values;
|
package ghidra.features.base.values;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import docking.Tool;
|
import docking.Tool;
|
||||||
import docking.widgets.values.GValuesMap;
|
import docking.widgets.values.GValuesMap;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
|
@ -23,12 +25,24 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends GValuesMap to add Ghidra specific types such as Address and Program
|
* Extends GValuesMap to add Ghidra specific types such as Address and Program
|
||||||
*/
|
*/
|
||||||
public class GhidraValuesMap extends GValuesMap {
|
public class GhidraValuesMap extends GValuesMap {
|
||||||
|
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a task monitor to be used when opening programs. Otherwise, {@link TaskMonitor#DUMMY} is
|
||||||
|
* used.
|
||||||
|
* @param monitor the TaskMonitor to use for opening programs
|
||||||
|
*/
|
||||||
|
public void setTaskMonitor(TaskMonitor monitor) {
|
||||||
|
this.monitor = TaskMonitor.dummyIfNull(monitor);
|
||||||
|
}
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Define Value Methods
|
// Define Value Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -88,36 +102,23 @@ public class GhidraValuesMap extends GValuesMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a value of type Program. This method opens programs using the given
|
* Defines a value of type Program file.
|
||||||
* 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 name the name for this value
|
||||||
* @param consumer the consumer to be used to open the program
|
* @return the new ProgramFileValue defined
|
||||||
* @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) {
|
public ProgramFileValue defineProgram(String name) {
|
||||||
return defineProgram(name, null, consumer, tool);
|
return defineProgram(name, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a value of type Program.
|
* Defines a value of type Program file.
|
||||||
* @param name the name for this value
|
* @param name the name for this value
|
||||||
* @param defaultValue the initial value
|
* @param startPath the starting folder to display when picking programs from the chooser
|
||||||
* @param consumer the consumer to be used to open the program
|
* @return the new ProgramFileValue that was defined
|
||||||
* @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,
|
public ProgramFileValue defineProgram(String name, String startPath) {
|
||||||
Tool tool) {
|
|
||||||
checkDup(name);
|
checkDup(name);
|
||||||
ProgramValue value = new ProgramValue(name, defaultValue, consumer, tool);
|
ProgramFileValue value = new ProgramFileValue(name, startPath);
|
||||||
valuesMap.put(name, value);
|
valuesMap.put(name, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -134,12 +135,12 @@ public class GhidraValuesMap extends GValuesMap {
|
||||||
/**
|
/**
|
||||||
* Defines a value of type DomainFile (files in a Ghidra project).
|
* Defines a value of type DomainFile (files in a Ghidra project).
|
||||||
* @param name the name for this value
|
* @param name the name for this value
|
||||||
* @param defaultValue the initial value
|
* @param startingPath the initial folder path for the chooser widget
|
||||||
* @return the new ProjectFileValue that was defined
|
* @return the new ProjectFileValue that was defined
|
||||||
*/
|
*/
|
||||||
public ProjectFileValue defineProjectFile(String name, DomainFile defaultValue) {
|
public ProjectFileValue defineProjectFile(String name, String startingPath) {
|
||||||
checkDup(name);
|
checkDup(name);
|
||||||
ProjectFileValue value = new ProjectFileValue(name, defaultValue);
|
ProjectFileValue value = new ProjectFileValue(name, startingPath);
|
||||||
valuesMap.put(name, value);
|
valuesMap.put(name, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -156,12 +157,12 @@ public class GhidraValuesMap extends GValuesMap {
|
||||||
/**
|
/**
|
||||||
* Defines a value of type DomainFolder (files in a Ghidra project).
|
* Defines a value of type DomainFolder (files in a Ghidra project).
|
||||||
* @param name the name for this value
|
* @param name the name for this value
|
||||||
* @param defaultValue the initial value (can be null)
|
* @param defaultValuePath the path for the initial value (can be null)
|
||||||
* @return the new ProjectFolderValue that was defined
|
* @return the new ProjectFolderValue that was defined
|
||||||
*/
|
*/
|
||||||
public ProjectFolderValue defineProjectFolder(String name, DomainFolder defaultValue) {
|
public ProjectFolderValue defineProjectFolder(String name, String defaultValuePath) {
|
||||||
checkDup(name);
|
checkDup(name);
|
||||||
ProjectFolderValue value = new ProjectFolderValue(name, defaultValue);
|
ProjectFolderValue value = new ProjectFolderValue(name, defaultValuePath);
|
||||||
valuesMap.put(name, value);
|
valuesMap.put(name, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -192,14 +193,33 @@ public class GhidraValuesMap extends GValuesMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link Program} value for the given name.
|
* Gets (opens) the {@link Program} value for the given name. If the program is already open,
|
||||||
* @param name the name of a previously defined project folder value
|
* then the consumer will be added to the program. The caller of this method is responsible
|
||||||
|
* for calling {@link Program#release(Object)} with the same consumer when it is done using this
|
||||||
|
* program. Program are only closed after all consumers are released. If multiple calls
|
||||||
|
* are made to this method, then the consumer will be added multiple times and must be released
|
||||||
|
* multiple times.
|
||||||
|
* <P>
|
||||||
|
* The consumer can be any object, but since the consumer's purpose is to keep the program open
|
||||||
|
* while some object is using it, the object itself is typically passed in as
|
||||||
|
* the consumer. For example, when used in a script, passing in the java keyword "this" as the
|
||||||
|
* consumer will make the script itself the consumer.
|
||||||
|
* <P>
|
||||||
|
* @param name the name of a previously defined program 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. Note: the
|
||||||
|
* program will only be added to the tool once even if this method is called multiple times.
|
||||||
* @return the project folder value
|
* @return the project folder value
|
||||||
|
* @throws VersionException if the Program being opened is an older version than the
|
||||||
|
* current Ghidra Program version.
|
||||||
|
* @throws IOException if there is an error accessing the Program's DomainObject
|
||||||
|
* @throws CancelledException if the operation is cancelled
|
||||||
* @throws IllegalArgumentException if the name hasn't been defined as a project folder type
|
* @throws IllegalArgumentException if the name hasn't been defined as a project folder type
|
||||||
*/
|
*/
|
||||||
public Program getProgram(String name) {
|
public Program getProgram(String name, Object consumer, Tool tool)
|
||||||
ProgramValue programValue = getValue(name, ProgramValue.class, "Program");
|
throws VersionException, IOException, CancelledException {
|
||||||
return programValue.getValue();
|
ProgramFileValue programFileValue = getValue(name, ProgramFileValue.class, "Program");
|
||||||
|
return programFileValue.openProgram(consumer, tool, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,8 +278,8 @@ public class GhidraValuesMap extends GValuesMap {
|
||||||
* @throws IllegalArgumentException if the name hasn't been defined as a Program type
|
* @throws IllegalArgumentException if the name hasn't been defined as a Program type
|
||||||
*/
|
*/
|
||||||
public void setProgram(String name, Program program) {
|
public void setProgram(String name, Program program) {
|
||||||
ProgramValue programValue = getValue(name, ProgramValue.class, "Program");
|
ProgramFileValue programFileValue = getValue(name, ProgramFileValue.class, "Program");
|
||||||
programValue.setValue(program);
|
programFileValue.setValue(program == null ? null : program.getDomainFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -91,33 +91,33 @@ public class LanguageValue extends AbstractValue<LanguageCompilerSpecPair> {
|
||||||
/**
|
/**
|
||||||
* Parses a LanguageCompilerSpecPair from a string.
|
* Parses a LanguageCompilerSpecPair from a string.
|
||||||
*
|
*
|
||||||
* @param val The string to parse.
|
* @param languageString The string to parse.
|
||||||
* @return The LanguageCompilerSpecPair parsed from a string or null if the string does
|
* @return The LanguageCompilerSpecPair parsed from a string or null if the string does
|
||||||
* not parse to a known language-compiler pair.
|
* not parse to a known language-compiler pair.
|
||||||
* @throws ValuesMapParseException
|
* @throws ValuesMapParseException if the value can't be parsed into a LanguageComilerSpecPair
|
||||||
*/
|
*/
|
||||||
public LanguageCompilerSpecPair parseLanguageCompileSpecPair(String val)
|
public LanguageCompilerSpecPair parseLanguageCompileSpecPair(String languageString)
|
||||||
throws ValuesMapParseException {
|
throws ValuesMapParseException {
|
||||||
|
|
||||||
if (val.isBlank()) {
|
if (languageString.isBlank()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Split on last colon to get separated languageID and compilerSpecID
|
// Split on last colon to get separated languageID and compilerSpecID
|
||||||
int lastColon = val.lastIndexOf(':');
|
int lastColon = languageString.lastIndexOf(':');
|
||||||
if (lastColon < 1) {
|
if (lastColon < 1) {
|
||||||
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
||||||
"Could not parse \"" + val + "\".");
|
"Could not parse \"" + languageString + "\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<LanguageCompilerSpecPair> languages = getLanguagesCompilerPairs();
|
Set<LanguageCompilerSpecPair> languages = getLanguagesCompilerPairs();
|
||||||
|
|
||||||
String langId = val.substring(0, lastColon);
|
String langId = languageString.substring(0, lastColon);
|
||||||
String compilerId = val.substring(lastColon + 1);
|
String compilerId = languageString.substring(lastColon + 1);
|
||||||
|
|
||||||
LanguageCompilerSpecPair storedLCS = new LanguageCompilerSpecPair(langId, compilerId);
|
LanguageCompilerSpecPair storedLCS = new LanguageCompilerSpecPair(langId, compilerId);
|
||||||
if (!languages.contains(storedLCS)) {
|
if (!languages.contains(storedLCS)) {
|
||||||
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
throw new ValuesMapParseException(getName(), "Language/Compiler Spec",
|
||||||
"Unknown language/Compiler Pair for \"" + val + "\"");
|
"Unknown language/Compiler Pair for \"" + languageString + "\"");
|
||||||
}
|
}
|
||||||
return storedLCS;
|
return storedLCS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* ###
|
||||||
|
* 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.JTextField;
|
||||||
|
|
||||||
|
import docking.Tool;
|
||||||
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
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} files. 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. This class also provides a convenience method for opening a 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 ProgramFileValue extends ProjectFileValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating a new ProgramFileValue with the given name.
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
|
public ProgramFileValue(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating a new ProgramFileValue with the given name and a starting
|
||||||
|
* folder when using the project file chooser.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param startingPath the path to a starting folder
|
||||||
|
*/
|
||||||
|
public ProgramFileValue(String name, String startingPath) {
|
||||||
|
this(name, AppInfo.getActiveProject(), startingPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProgramValue when wanting to pick from a different project than the
|
||||||
|
* active project, such as a read-only project.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param project The project from which to pick a project.
|
||||||
|
* @param startingPath the path to a starting folder (Can also be a path to program)
|
||||||
|
*/
|
||||||
|
public ProgramFileValue(String name, Project project, String startingPath) {
|
||||||
|
super(name, project, startingPath, Program.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for opening the program for the current program file value. If the program
|
||||||
|
* is already open, then the consumer will be added to the program. The caller of this method is
|
||||||
|
* responsible for calling {@link Program#release(Object)} with the same consumer when it is
|
||||||
|
* done using this program. Program are only closed after all consumers are released. If
|
||||||
|
* multiple calls are made to this method, then the consumer will be added multiple times
|
||||||
|
* and must be released multiple times.
|
||||||
|
* <P>
|
||||||
|
* The consumer can be any object, but since the consumer's purpose is to keep the program open
|
||||||
|
* while some object is using it, the object itself is typically passed in as
|
||||||
|
* the consumer. For example, when used in a script, passing in the java keyword "this" as the
|
||||||
|
* consumer will make the script itself the consumer.
|
||||||
|
* <P>
|
||||||
|
* @param consumer the consumer to be used to open the program
|
||||||
|
* @param tool optional tool that if non-null, the program will also be opened in the tool
|
||||||
|
* @param monitor task monitor for cancelling the open program.
|
||||||
|
* @return a program for the current program value value. If the current program file value
|
||||||
|
* is null, then null will be returned.
|
||||||
|
* @throws VersionException if the Program being opened is an older version than the
|
||||||
|
* current Ghidra Program version.
|
||||||
|
* @throws IOException if there is an error accessing the Program's DomainObject
|
||||||
|
* @throws CancelledException if the operation is cancelled
|
||||||
|
*/
|
||||||
|
public Program openProgram(Object consumer, Tool tool, TaskMonitor monitor)
|
||||||
|
throws VersionException, IOException, CancelledException {
|
||||||
|
|
||||||
|
DomainFile domainFile = getValue();
|
||||||
|
if (domainFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program program = (Program) domainFile.getDomainObject(consumer, false, false, monitor);
|
||||||
|
|
||||||
|
if (tool != null && program != null) {
|
||||||
|
tool.getService(ProgramManager.class).openProgram(program);
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,153 +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.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,12 +21,11 @@ import javax.swing.JTextField;
|
||||||
import docking.widgets.values.*;
|
import docking.widgets.values.*;
|
||||||
import ghidra.framework.main.AppInfo;
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.model.Project;
|
|
||||||
import ghidra.framework.store.FileSystem;
|
import ghidra.framework.store.FileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value class for project files ({@link DomainFile}). The editor component consists of the
|
* Value class for project files ({@link DomainFile}). The editor component consists of a
|
||||||
* {@link JTextField} and a browse button for bringing up a {@link DataTreeDialog} for picking
|
* {@link JTextField} and a browse button for bringing up a {@link DataTreeDialog} for picking
|
||||||
* project files from the current project.
|
* project files from the current project.
|
||||||
* <P>
|
* <P>
|
||||||
|
@ -39,20 +38,61 @@ import ghidra.framework.store.FileSystem;
|
||||||
*/
|
*/
|
||||||
public class ProjectFileValue extends AbstractValue<DomainFile> {
|
public class ProjectFileValue extends AbstractValue<DomainFile> {
|
||||||
|
|
||||||
private ProjectBrowserPanel domainFilePanel;
|
private ProjectFileBrowserPanel domainFilePanel;
|
||||||
|
private String startingPath;
|
||||||
|
private Project project;
|
||||||
|
private Class<? extends DomainObject> projectFileClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating a new ProjectFileValue with the given name.
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
public ProjectFileValue(String name) {
|
public ProjectFileValue(String name) {
|
||||||
this(name, null);
|
this(name, AppInfo.getActiveProject(), null, DomainObject.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectFileValue(String name, DomainFile defaultValue) {
|
/**
|
||||||
super(name, defaultValue);
|
* Constructor for creating a new ProgramFileValue with the given name and {@link DomainObject}
|
||||||
|
* class to filter on (All other types will be filtered out in the chooser).
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param projectFileClass the DomainObject class to filter
|
||||||
|
*/
|
||||||
|
public ProjectFileValue(String name, Class<? extends DomainObject> projectFileClass) {
|
||||||
|
this(name, AppInfo.getActiveProject(), null, projectFileClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating a new ProjectFileValue with the given name and a starting
|
||||||
|
* folder when using the project file chooser.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param startingPath the path to a starting folder
|
||||||
|
*/
|
||||||
|
public ProjectFileValue(String name, String startingPath) {
|
||||||
|
this(name, AppInfo.getActiveProject(), startingPath, DomainObject.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProgramValue when wanting to pick from a different project than the
|
||||||
|
* active project, such as a read-only project.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param project The project from which to pick a project.
|
||||||
|
* @param startingPath the path to a starting folder (Can also be a path to program)
|
||||||
|
* @param projectFileClass a {@link DomainFile} class to filter on. (Only those types
|
||||||
|
* will appear in the chooser)
|
||||||
|
*/
|
||||||
|
public ProjectFileValue(String name, Project project, String startingPath,
|
||||||
|
Class<? extends DomainObject> projectFileClass) {
|
||||||
|
super(name, null);
|
||||||
|
this.project = project;
|
||||||
|
this.startingPath = startingPath;
|
||||||
|
this.projectFileClass = projectFileClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
if (domainFilePanel == null) {
|
if (domainFilePanel == null) {
|
||||||
domainFilePanel = new ProjectBrowserPanel(getName(), false);
|
domainFilePanel =
|
||||||
|
new ProjectFileBrowserPanel(project, getName(), startingPath, projectFileClass);
|
||||||
}
|
}
|
||||||
return domainFilePanel;
|
return domainFilePanel;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +110,11 @@ public class ProjectFileValue extends AbstractValue<DomainFile> {
|
||||||
throw new ValuesMapParseException(getName(), "Project File",
|
throw new ValuesMapParseException(getName(), "Project File",
|
||||||
"No file found for \"" + text + "\"");
|
"No file found for \"" + text + "\"");
|
||||||
}
|
}
|
||||||
|
Class<? extends DomainObject> domainObjectClass = domainFile.getDomainObjectClass();
|
||||||
|
if (!projectFileClass.isAssignableFrom(domainObjectClass)) {
|
||||||
|
throw new ValuesMapParseException(getName(), "Project File",
|
||||||
|
"Selected path is not a " + projectFileClass.getSimpleName());
|
||||||
|
}
|
||||||
setValue(domainFile);
|
setValue(domainFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +132,11 @@ public class ProjectFileValue extends AbstractValue<DomainFile> {
|
||||||
if (df == null) {
|
if (df == null) {
|
||||||
throw new IllegalArgumentException("Can't find domain file: " + valueString);
|
throw new IllegalArgumentException("Can't find domain file: " + valueString);
|
||||||
}
|
}
|
||||||
|
Class<? extends DomainObject> domainObjectClass = df.getDomainObjectClass();
|
||||||
|
if (!projectFileClass.isAssignableFrom(domainObjectClass)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Specified file path is not a " + projectFileClass.getSimpleName());
|
||||||
|
}
|
||||||
return df;
|
return df;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,4 +160,44 @@ public class ProjectFileValue extends AbstractValue<DomainFile> {
|
||||||
protected String toString(DomainFile v) {
|
protected String toString(DomainFile v) {
|
||||||
return v.getPathname();
|
return v.getPathname();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used by ProjectFileValues for picking project files
|
||||||
|
*/
|
||||||
|
class ProjectFileBrowserPanel extends AbstractProjectBrowserPanel {
|
||||||
|
|
||||||
|
ProjectFileBrowserPanel(Project project, String name, String startPath,
|
||||||
|
Class<? extends DomainObject> projectFileClass) {
|
||||||
|
super(DataTreeDialog.OPEN, project, name, startPath);
|
||||||
|
filter = df -> projectFileClass.isAssignableFrom(df.getDomainObjectClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainFile(DomainFile value) {
|
||||||
|
String text = value == null ? "" : value.getPathname();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void intializeCurrentValue(DataTreeDialog dialog) {
|
||||||
|
DomainFile current = getDomainFile();
|
||||||
|
if (current != null) {
|
||||||
|
dialog.selectDomainFile(current);
|
||||||
|
dialog.setNameText(current.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSelectedPath(DataTreeDialog dialog) {
|
||||||
|
return dialog.getDomainFile().getPathname();
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainFile getDomainFile() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parseDomainFile(project, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ import javax.swing.JComponent;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
import docking.widgets.values.*;
|
import docking.widgets.values.*;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value class for project folders ({@link DomainFile}). The editor component consists of the
|
* Value class for project folders ({@link DomainFile}). The editor component consists of the
|
||||||
|
@ -37,20 +37,42 @@ import ghidra.framework.model.DomainFolder;
|
||||||
*/
|
*/
|
||||||
public class ProjectFolderValue extends AbstractValue<DomainFolder> {
|
public class ProjectFolderValue extends AbstractValue<DomainFolder> {
|
||||||
|
|
||||||
private ProjectBrowserPanel domainFilePanel;
|
private Project project;
|
||||||
|
private ProjectFolderBrowserPanel domainFilePanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProjectFolderValues with the given name.
|
||||||
|
* @param name the name of the value
|
||||||
|
*/
|
||||||
public ProjectFolderValue(String name) {
|
public ProjectFolderValue(String name) {
|
||||||
this(name, null);
|
this(name, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectFolderValue(String name, DomainFolder defaultValue) {
|
/**
|
||||||
super(name, defaultValue);
|
* Constructor for creating a new ProjectFolderValue with the given name and a path
|
||||||
|
* for a default folder value.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param defaultValuePath the path for a default folder value
|
||||||
|
*/
|
||||||
|
public ProjectFolderValue(String name, String defaultValuePath) {
|
||||||
|
this(name, AppInfo.getActiveProject(), defaultValuePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating ProjectFolderValues for projects other than the active project.
|
||||||
|
* @param name the name of the value
|
||||||
|
* @param project the project to find a folder from
|
||||||
|
* @param defaultValuePath the path of a default folder value
|
||||||
|
*/
|
||||||
|
public ProjectFolderValue(String name, Project project, String defaultValuePath) {
|
||||||
|
super(name, AbstractProjectBrowserPanel.parseDomainFolder(project, defaultValuePath));
|
||||||
|
this.project = project;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
if (domainFilePanel == null) {
|
if (domainFilePanel == null) {
|
||||||
domainFilePanel = new ProjectBrowserPanel(getName(), true);
|
domainFilePanel = new ProjectFolderBrowserPanel(project, getName(), null);
|
||||||
}
|
}
|
||||||
return domainFilePanel;
|
return domainFilePanel;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +105,7 @@ public class ProjectFolderValue extends AbstractValue<DomainFolder> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DomainFolder fromString(String valueString) {
|
protected DomainFolder fromString(String valueString) {
|
||||||
DomainFolder df = ProjectBrowserPanel.parseDomainFolder(valueString);
|
DomainFolder df = AbstractProjectBrowserPanel.parseDomainFolder(project, valueString);
|
||||||
if (df == null) {
|
if (df == null) {
|
||||||
throw new IllegalArgumentException("Can't find domain folder: " + valueString);
|
throw new IllegalArgumentException("Can't find domain folder: " + valueString);
|
||||||
}
|
}
|
||||||
|
@ -95,4 +117,38 @@ public class ProjectFolderValue extends AbstractValue<DomainFolder> {
|
||||||
return v.getPathname();
|
return v.getPathname();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used by ProjectFolderValues for picking project folders
|
||||||
|
*/
|
||||||
|
class ProjectFolderBrowserPanel extends AbstractProjectBrowserPanel {
|
||||||
|
|
||||||
|
ProjectFolderBrowserPanel(Project project, String name, String startPath) {
|
||||||
|
super(DataTreeDialog.CHOOSE_FOLDER, project, name, startPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainFolder(DomainFolder value) {
|
||||||
|
String text = value == null ? "" : value.getPathname();
|
||||||
|
textField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void intializeCurrentValue(DataTreeDialog dialog) {
|
||||||
|
DomainFolder current = getDomainFolder();
|
||||||
|
dialog.selectFolder(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSelectedPath(DataTreeDialog dialog) {
|
||||||
|
return dialog.getDomainFolder().getPathname();
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainFolder getDomainFolder() {
|
||||||
|
String text = textField.getText().trim();
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return parseDomainFolder(project, "/");
|
||||||
|
}
|
||||||
|
return parseDomainFolder(project, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,10 +91,11 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
private ProjectDataNewFolderAction<DialogProjectTreeContext> newFolderAction;
|
private ProjectDataNewFolderAction<DialogProjectTreeContext> newFolderAction;
|
||||||
|
|
||||||
private Integer treeSelectionMode;
|
private Integer treeSelectionMode;
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new DataTreeDialog. This chooser will show all project files.
|
* Construct a new DataTreeDialog for the active project. This chooser will show all project
|
||||||
* Following linked-folders will only be allowed if a type of {@link #CHOOSE_FOLDER}
|
* files. Following linked-folders will only be allowed if a type of {@link #CHOOSE_FOLDER}
|
||||||
* or {@link #OPEN} is specified. If different behavior is required a filter should
|
* or {@link #OPEN} is specified. If different behavior is required a filter should
|
||||||
* be specified using the other constructor.
|
* be specified using the other constructor.
|
||||||
*
|
*
|
||||||
|
@ -104,11 +105,11 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
* @throws IllegalArgumentException if invalid type is specified
|
* @throws IllegalArgumentException if invalid type is specified
|
||||||
*/
|
*/
|
||||||
public DataTreeDialog(Component parent, String title, int type) {
|
public DataTreeDialog(Component parent, String title, int type) {
|
||||||
this(parent, title, type, getDefaultFilter(type));
|
this(parent, title, type, getDefaultFilter(type), AppInfo.getActiveProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new DataTreeDialog.
|
* Construct a new DataTreeDialog for the active project.
|
||||||
*
|
*
|
||||||
* @param parent dialog's parent
|
* @param parent dialog's parent
|
||||||
* @param title title to use
|
* @param title title to use
|
||||||
|
@ -117,7 +118,23 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
* @throws IllegalArgumentException if invalid type is specified
|
* @throws IllegalArgumentException if invalid type is specified
|
||||||
*/
|
*/
|
||||||
public DataTreeDialog(Component parent, String title, int type, DomainFileFilter filter) {
|
public DataTreeDialog(Component parent, String title, int type, DomainFileFilter filter) {
|
||||||
|
this(parent, title, type, filter, AppInfo.getActiveProject());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new DataTreeDialog for the given project.
|
||||||
|
*
|
||||||
|
* @param parent dialog's parent
|
||||||
|
* @param title title to use
|
||||||
|
* @param type specify OPEN, SAVE, CHOOSE_FOLDER, or CHOOSE_USER_FOLDER
|
||||||
|
* @param filter filter used to control what is displayed in the data tree
|
||||||
|
* @param project the project to browse
|
||||||
|
* @throws IllegalArgumentException if invalid type is specified
|
||||||
|
*/
|
||||||
|
public DataTreeDialog(Component parent, String title, int type, DomainFileFilter filter,
|
||||||
|
Project project) {
|
||||||
super(title, true, true, true, false);
|
super(title, true, true, true, false);
|
||||||
|
this.project = project;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
initDataTreeDialog(type, filter);
|
initDataTreeDialog(type, filter);
|
||||||
}
|
}
|
||||||
|
@ -230,7 +247,6 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
|
|
||||||
comboModelInitialized = true;
|
comboModelInitialized = true;
|
||||||
// repopulate the tree
|
// repopulate the tree
|
||||||
Project project = AppInfo.getActiveProject();
|
|
||||||
ProjectData pd = project.getProjectData();
|
ProjectData pd = project.getProjectData();
|
||||||
treePanel.setProjectData(project.getName(), pd);
|
treePanel.setProjectData(project.getName(), pd);
|
||||||
|
|
||||||
|
@ -373,7 +389,7 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
nameField.setText(domainFolder.getName());
|
nameField.setText(domainFolder.getName());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
domainFolder = AppInfo.getActiveProject().getProjectData().getRootFolder();
|
domainFolder = project.getProjectData().getRootFolder();
|
||||||
folderNameLabel.setText(domainFolder.getPathname());
|
folderNameLabel.setText(domainFolder.getPathname());
|
||||||
nameField.setText(domainFolder.getName());
|
nameField.setText(domainFolder.getName());
|
||||||
}
|
}
|
||||||
|
@ -389,7 +405,7 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
else {
|
else {
|
||||||
domainFolder = treePanel.getSelectedDomainFolder();
|
domainFolder = treePanel.getSelectedDomainFolder();
|
||||||
if (domainFolder == null) {
|
if (domainFolder == null) {
|
||||||
domainFolder = AppInfo.getActiveProject().getProjectData().getRootFolder();
|
domainFolder = project.getProjectData().getRootFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
folderNameLabel.setText(domainFolder.getPathname());
|
folderNameLabel.setText(domainFolder.getPathname());
|
||||||
|
@ -415,7 +431,6 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Project project = AppInfo.getActiveProject();
|
|
||||||
ProjectLocator projectLocator = projectLocators[index];
|
ProjectLocator projectLocator = projectLocators[index];
|
||||||
ProjectData pd = project.getProjectData(projectLocator);
|
ProjectData pd = project.getProjectData(projectLocator);
|
||||||
String projectName = projectLocator.getName();
|
String projectName = projectLocator.getName();
|
||||||
|
@ -511,7 +526,6 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
|
|
||||||
JPanel panel = new JPanel();
|
JPanel panel = new JPanel();
|
||||||
panel.setLayout(new BorderLayout());
|
panel.setLayout(new BorderLayout());
|
||||||
Project project = AppInfo.getActiveProject();
|
|
||||||
ProjectData pd = project.getProjectData();
|
ProjectData pd = project.getProjectData();
|
||||||
|
|
||||||
treePanel = new ProjectDataTreePanel(project.getName(), true, // is for the active project
|
treePanel = new ProjectDataTreePanel(project.getName(), true, // is for the active project
|
||||||
|
@ -646,7 +660,6 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateProjectModel() {
|
private void populateProjectModel() {
|
||||||
Project project = AppInfo.getActiveProject();
|
|
||||||
ProjectLocator[] views = project.getProjectViews();
|
ProjectLocator[] views = project.getProjectViews();
|
||||||
|
|
||||||
projectLocators = new ProjectLocator[views.length + 1];
|
projectLocators = new ProjectLocator[views.length + 1];
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.features.base.values;
|
package ghidra.features.base.values;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
@ -25,13 +27,14 @@ import docking.DialogComponentProvider;
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.widgets.values.AbstractValue;
|
import docking.widgets.values.AbstractValue;
|
||||||
import docking.widgets.values.ValuesMapDialog;
|
import docking.widgets.values.ValuesMapDialog;
|
||||||
import ghidra.features.base.values.GhidraValuesMap;
|
import ghidra.features.base.values.ProjectFileValue.ProjectFileBrowserPanel;
|
||||||
import ghidra.features.base.values.ProjectBrowserPanel;
|
import ghidra.features.base.values.ProjectFolderValue.ProjectFolderBrowserPanel;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -43,30 +46,47 @@ public abstract class AbstractValueIntegrationTest extends AbstractGhidraHeadedI
|
||||||
|
|
||||||
protected TestEnv env;
|
protected TestEnv env;
|
||||||
protected DomainFolder rootFolder;
|
protected DomainFolder rootFolder;
|
||||||
protected DomainFolder folder;
|
protected DomainFolder folderA;
|
||||||
|
protected DomainFolder folderB;
|
||||||
protected DomainFile fileA;
|
protected DomainFile fileA;
|
||||||
protected DomainFile fileB;
|
protected DomainFile fileB;
|
||||||
protected ProgramDB programA;
|
protected DomainFile fileC;
|
||||||
protected ProgramDB programB;
|
protected Program programA;
|
||||||
|
protected Program programB;
|
||||||
|
protected Project project;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
env = new TestEnv();
|
env = new TestEnv();
|
||||||
Project project = env.getProject();
|
project = env.getProject();
|
||||||
runSwing(() -> env.getFrontEndTool().setActiveProject(project));
|
AppInfo.setActiveProject(project);
|
||||||
rootFolder = project.getProjectData().getRootFolder();
|
rootFolder = project.getProjectData().getRootFolder();
|
||||||
folder = rootFolder.createFolder("A");
|
folderA = rootFolder.createFolder("A");
|
||||||
ProgramBuilder programBuilderA = new ProgramBuilder("A", ProgramBuilder._TOY);
|
folderB = rootFolder.createFolder("B");
|
||||||
ProgramBuilder programBuilderB = new ProgramBuilder("B", ProgramBuilder._TOY);
|
ProgramBuilder programBuilderA = new ProgramBuilder("A", ProgramBuilder._TOY, this);
|
||||||
|
ProgramBuilder programBuilderB = new ProgramBuilder("B", ProgramBuilder._TOY, this);
|
||||||
|
ProgramBuilder programBuilderC = new ProgramBuilder("C", ProgramBuilder._TOY, this);
|
||||||
programA = programBuilderA.getProgram();
|
programA = programBuilderA.getProgram();
|
||||||
programB = programBuilderB.getProgram();
|
programB = programBuilderB.getProgram();
|
||||||
fileA = folder.createFile("A", programA, TaskMonitor.DUMMY);
|
Program programC = programBuilderC.getProgram();
|
||||||
fileB = folder.createFile("B", programB, TaskMonitor.DUMMY);
|
fileA = folderA.createFile("A", programA, TaskMonitor.DUMMY);
|
||||||
|
fileB = folderA.createFile("B", programB, TaskMonitor.DUMMY);
|
||||||
|
fileC = folderA.createFile("C", programC, TaskMonitor.DUMMY);
|
||||||
|
programBuilderC.dispose(); // closes program C
|
||||||
|
programC.release(this);
|
||||||
|
assertTrue(programC.isClosed());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
|
// some tests close the programs
|
||||||
|
if (!programA.isClosed()) {
|
||||||
|
programA.release(this);
|
||||||
|
}
|
||||||
|
if (!programB.isClosed()) {
|
||||||
|
programB.release(this);
|
||||||
|
}
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +111,7 @@ public abstract class AbstractValueIntegrationTest extends AbstractGhidraHeadedI
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setProjectFileOnProjectTree(AbstractValue<?> value, DomainFile file) {
|
protected void setProjectFileOnProjectTree(AbstractValue<?> value, DomainFile file) {
|
||||||
ProjectBrowserPanel projectWidget = (ProjectBrowserPanel) value.getComponent();
|
ProjectFileBrowserPanel projectWidget = (ProjectFileBrowserPanel) value.getComponent();
|
||||||
pressButtonByName(projectWidget, "BrowseButton", false);
|
pressButtonByName(projectWidget, "BrowseButton", false);
|
||||||
|
|
||||||
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
||||||
|
@ -104,7 +124,7 @@ public abstract class AbstractValueIntegrationTest extends AbstractGhidraHeadedI
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setProjectFolderOnProjectTree(AbstractValue<?> value, DomainFolder folder) {
|
protected void setProjectFolderOnProjectTree(AbstractValue<?> value, DomainFolder folder) {
|
||||||
ProjectBrowserPanel projectWidget = (ProjectBrowserPanel) value.getComponent();
|
ProjectFolderBrowserPanel projectWidget = (ProjectFolderBrowserPanel) value.getComponent();
|
||||||
pressButtonByName(projectWidget, "BrowseButton", false);
|
pressButtonByName(projectWidget, "BrowseButton", false);
|
||||||
|
|
||||||
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
DataTreeDialog dataTreeDialog = waitForDialogComponent(DataTreeDialog.class);
|
||||||
|
|
|
@ -20,16 +20,15 @@ import static org.junit.Assert.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.features.base.values.ProgramValue;
|
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class ProgramValueTest extends AbstractValueIntegrationTest {
|
public class ProgramFileValueTest extends AbstractValueIntegrationTest {
|
||||||
private static final String NAME = "Program";
|
private static final String NAME = "Program";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProgramValueNoDefault() {
|
public void testProgramValueNoDefault() throws Exception {
|
||||||
values.defineProgram(NAME, this, null);
|
values.defineProgram(NAME);
|
||||||
|
|
||||||
assertTrue(values.isDefined(NAME));
|
assertTrue(values.isDefined(NAME));
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
@ -37,38 +36,41 @@ public class ProgramValueTest extends AbstractValueIntegrationTest {
|
||||||
values.setProgram(NAME, programA);
|
values.setProgram(NAME, programA);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
assertEquals(programA, values.getProgram(NAME));
|
assertEquals(programA, values.getProgram(NAME, this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProgramValueWithDefault() {
|
public void testSetValue() throws Exception {
|
||||||
values.defineProgram(NAME, programA, this, null);
|
values.defineProgram(NAME);
|
||||||
|
values.setProgram(NAME, programA);
|
||||||
|
|
||||||
assertTrue(values.isDefined(NAME));
|
assertTrue(values.isDefined(NAME));
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(programA, values.getProgram(NAME));
|
assertEquals(programA, values.getProgram(NAME, this, null));
|
||||||
|
|
||||||
values.setProgram(NAME, programB);
|
values.setProgram(NAME, programB);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
assertEquals(programB, values.getProgram(NAME));
|
assertEquals(programB, values.getProgram(NAME, this, null));
|
||||||
|
|
||||||
values.setProgram(NAME, null);
|
values.setProgram(NAME, null);
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAsText() {
|
public void testGetAsText() throws Exception {
|
||||||
ProgramValue value1 = new ProgramValue(NAME, this, null);
|
ProgramFileValue value = values.defineProgram(NAME);
|
||||||
ProgramValue value2 = new ProgramValue(NAME, programA, this, null);
|
assertNull(value.getAsText());
|
||||||
assertNull(value1.getAsText());
|
|
||||||
assertEquals("/A/A", value2.getAsText());
|
values.setProgram(NAME, programA);
|
||||||
|
|
||||||
|
assertEquals("/A/A", value.getAsText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetAsText() {
|
public void testSetAsText() {
|
||||||
ProgramValue v = new ProgramValue(NAME, this, null);
|
ProgramFileValue v = new ProgramFileValue(NAME);
|
||||||
assertEquals(programA, v.setAsText("/A/A"));
|
assertEquals(programA.getDomainFile(), v.setAsText("/A/A"));
|
||||||
try {
|
try {
|
||||||
v.setAsText(null);
|
v.setAsText(null);
|
||||||
fail("Expected exception");
|
fail("Expected exception");
|
||||||
|
@ -86,20 +88,20 @@ public class ProgramValueTest extends AbstractValueIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoDefaultValueWithNoDialogInput() {
|
public void testNoDefaultValueWithNoDialogInput() throws Exception {
|
||||||
values.defineProgram(NAME, this, null);
|
values.defineProgram(NAME);
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
assertNull(values.getProgram(NAME));
|
assertNull(values.getProgram(NAME, this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoDefaultValueWithDialogInput() {
|
public void testNoDefaultValueWithDialogInput() throws Exception {
|
||||||
values.defineProgram(NAME, this, null);
|
values.defineProgram(NAME);
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
@ -107,49 +109,67 @@ public class ProgramValueTest extends AbstractValueIntegrationTest {
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(programA, values.getProgram(NAME));
|
assertEquals(programA, values.getProgram(NAME, this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithNoDialogInput() {
|
public void testExistingValueWithNoDialogInput() throws Exception {
|
||||||
values.defineProgram(NAME, programA, this, null);
|
values.defineProgram(NAME);
|
||||||
assertTrue(values.hasValue(NAME));
|
values.setProgram(NAME, programA);
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(programA, values.getProgram(NAME));
|
assertEquals(programA, values.getProgram(NAME, this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithDialogInput() {
|
public void testDefaultValueWithDialogInput() throws Exception {
|
||||||
values.defineProgram(NAME, programA, this, null);
|
values.defineProgram(NAME);
|
||||||
assertTrue(values.hasValue(NAME));
|
values.setProgram(NAME, programA);
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programB.getDomainFile());
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programB.getDomainFile());
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(programB, values.getProgram(NAME));
|
assertEquals(programB, values.getProgram(NAME, this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpenProgramInTool() {
|
public void testOpenProgramInTool() throws Exception {
|
||||||
PluginTool tool = env.createDefaultTool();
|
PluginTool tool = env.createDefaultTool();
|
||||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||||
Program[] allOpenPrograms = programManagerService.getAllOpenPrograms();
|
Program[] allOpenPrograms = programManagerService.getAllOpenPrograms();
|
||||||
assertEquals(0, allOpenPrograms.length);
|
assertEquals(0, allOpenPrograms.length);
|
||||||
|
|
||||||
values.defineProgram(NAME, this, tool);
|
values.defineProgram(NAME);
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile());
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile());
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
|
Program p = values.getProgram(NAME, this, tool);
|
||||||
|
|
||||||
allOpenPrograms = programManagerService.getAllOpenPrograms();
|
allOpenPrograms = programManagerService.getAllOpenPrograms();
|
||||||
assertEquals(1, allOpenPrograms.length);
|
assertEquals(1, allOpenPrograms.length);
|
||||||
assertEquals(programA, allOpenPrograms[0]);
|
assertEquals(p, allOpenPrograms[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOpenProgramMutltipleTimes() throws Exception {
|
||||||
|
values.defineProgram(NAME);
|
||||||
|
assertEquals(1, programA.getConsumerList().size());
|
||||||
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile());
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
Program p1 = values.getProgram(NAME, this, null);
|
||||||
|
assertEquals(2, programA.getConsumerList().size());
|
||||||
|
Program p2 = values.getProgram(NAME, this, null);
|
||||||
|
assertEquals(p1, p2);
|
||||||
|
assertEquals(3, programA.getConsumerList().size());
|
||||||
|
p1.release(this);
|
||||||
|
p2.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,8 +19,6 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.features.base.values.ProjectFileValue;
|
|
||||||
|
|
||||||
public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
private static final String NAME = "Project File";
|
private static final String NAME = "Project File";
|
||||||
|
|
||||||
|
@ -39,7 +37,8 @@ public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProjectFileValueWithDefault() {
|
public void testProjectFileValueWithDefault() {
|
||||||
values.defineProjectFile(NAME, fileA);
|
values.defineProjectFile(NAME);
|
||||||
|
values.setProjectFile(NAME, fileA);
|
||||||
|
|
||||||
assertTrue(values.isDefined(NAME));
|
assertTrue(values.isDefined(NAME));
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
@ -57,7 +56,9 @@ public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testGetAsText() {
|
public void testGetAsText() {
|
||||||
ProjectFileValue value1 = new ProjectFileValue(NAME);
|
ProjectFileValue value1 = new ProjectFileValue(NAME);
|
||||||
ProjectFileValue value2 = new ProjectFileValue(NAME, fileA);
|
ProjectFileValue value2 = new ProjectFileValue(NAME);
|
||||||
|
value2.setValue(fileA);
|
||||||
|
|
||||||
assertNull(value1.getAsText());
|
assertNull(value1.getAsText());
|
||||||
assertEquals("/A/A", value2.getAsText());
|
assertEquals("/A/A", value2.getAsText());
|
||||||
}
|
}
|
||||||
|
@ -109,7 +110,8 @@ public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithNoDialogInput() {
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
values.defineProjectFile(NAME, fileA);
|
values.defineProjectFile(NAME);
|
||||||
|
values.setProjectFile(NAME, fileA);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
@ -121,7 +123,8 @@ public class ProjectFileValueTest extends AbstractValueIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithDialogInput() {
|
public void testDefaultValueWithDialogInput() {
|
||||||
values.defineProjectFile(NAME, fileA);
|
values.defineProjectFile(NAME);
|
||||||
|
values.setProjectFile(NAME, fileA);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
|
|
|
@ -19,8 +19,6 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.features.base.values.ProjectFolderValue;
|
|
||||||
|
|
||||||
public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
private static final String NAME = "Project File";
|
private static final String NAME = "Project File";
|
||||||
|
|
||||||
|
@ -31,24 +29,24 @@ public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
assertTrue(values.isDefined(NAME));
|
assertTrue(values.isDefined(NAME));
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
values.setProjectFolder(NAME, folder);
|
values.setProjectFolder(NAME, folderA);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
assertEquals(folder, values.getProjectFolder(NAME));
|
assertEquals(folderA, values.getProjectFolder(NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProjectFolderValueWithDefault() {
|
public void testProjectFolderValueWithDefault() {
|
||||||
values.defineProjectFolder(NAME, rootFolder);
|
values.defineProjectFolder(NAME, "/A");
|
||||||
|
|
||||||
assertTrue(values.isDefined(NAME));
|
assertTrue(values.isDefined(NAME));
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(rootFolder, values.getProjectFolder(NAME));
|
assertEquals(folderA, values.getProjectFolder(NAME));
|
||||||
|
|
||||||
values.setProjectFolder(NAME, folder);
|
values.setProjectFolder(NAME, folderB);
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
assertEquals(folder, values.getProjectFolder(NAME));
|
assertEquals(folderB, values.getProjectFolder(NAME));
|
||||||
|
|
||||||
values.setProjectFolder(NAME, null);
|
values.setProjectFolder(NAME, null);
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
@ -57,7 +55,7 @@ public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testGetAsText() {
|
public void testGetAsText() {
|
||||||
ProjectFolderValue value1 = new ProjectFolderValue(NAME);
|
ProjectFolderValue value1 = new ProjectFolderValue(NAME);
|
||||||
ProjectFolderValue value2 = new ProjectFolderValue(NAME, folder);
|
ProjectFolderValue value2 = new ProjectFolderValue(NAME, "/A");
|
||||||
assertNull(value1.getAsText());
|
assertNull(value1.getAsText());
|
||||||
assertEquals("/A", value2.getAsText());
|
assertEquals("/A", value2.getAsText());
|
||||||
}
|
}
|
||||||
|
@ -65,7 +63,7 @@ public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSetAsText() {
|
public void testSetAsText() {
|
||||||
ProjectFolderValue v = new ProjectFolderValue(NAME);
|
ProjectFolderValue v = new ProjectFolderValue(NAME);
|
||||||
assertEquals(folder, v.setAsText("/A"));
|
assertEquals(folderA, v.setAsText("/A"));
|
||||||
try {
|
try {
|
||||||
v.setAsText(null);
|
v.setAsText(null);
|
||||||
fail("Expected exception");
|
fail("Expected exception");
|
||||||
|
@ -102,36 +100,36 @@ public class ProjectFolderValueTest extends AbstractValueIntegrationTest {
|
||||||
assertFalse(values.hasValue(NAME));
|
assertFalse(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folder);
|
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folderA);
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(folder, values.getProjectFolder(NAME));
|
assertEquals(folderA, values.getProjectFolder(NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithNoDialogInput() {
|
public void testDefaultValueWithNoDialogInput() {
|
||||||
values.defineProjectFolder(NAME, folder);
|
values.defineProjectFolder(NAME, "/A");
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(folder, values.getProjectFolder(NAME));
|
assertEquals(folderA, values.getProjectFolder(NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultValueWithDialogInput() {
|
public void testDefaultValueWithDialogInput() {
|
||||||
values.defineProjectFolder(NAME, rootFolder);
|
values.defineProjectFolder(NAME, "/A");
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
|
|
||||||
showDialogOnSwingWithoutBlocking();
|
showDialogOnSwingWithoutBlocking();
|
||||||
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folder);
|
setProjectFolderOnProjectTree(values.getAbstractValue(NAME), folderB);
|
||||||
pressOk();
|
pressOk();
|
||||||
|
|
||||||
assertTrue(values.hasValue(NAME));
|
assertTrue(values.hasValue(NAME));
|
||||||
assertEquals(folder, values.getProjectFolder(NAME));
|
assertEquals(folderB, values.getProjectFolder(NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import javax.swing.JComponent;
|
||||||
public abstract class AbstractValue<T> {
|
public abstract class AbstractValue<T> {
|
||||||
private final String name;
|
private final String name;
|
||||||
private T value;
|
private T value;
|
||||||
|
private T originalValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor that assigned a name and optional initial value for this object.
|
* Constructor that assigned a name and optional initial value for this object.
|
||||||
|
@ -49,6 +50,7 @@ public abstract class AbstractValue<T> {
|
||||||
protected AbstractValue(String name, T defaultValue) {
|
protected AbstractValue(String name, T defaultValue) {
|
||||||
this.name = Objects.requireNonNull(name);
|
this.name = Objects.requireNonNull(name);
|
||||||
this.value = defaultValue;
|
this.value = defaultValue;
|
||||||
|
this.originalValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,6 +118,13 @@ public abstract class AbstractValue<T> {
|
||||||
return value == null ? null : toString(value);
|
return value == null ? null : toString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the value to its original value when constructed
|
||||||
|
*/
|
||||||
|
protected void reset() {
|
||||||
|
value = originalValue;
|
||||||
|
}
|
||||||
|
|
||||||
protected String toString(T t) {
|
protected String toString(T t) {
|
||||||
return t.toString();
|
return t.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,4 +553,14 @@ public class GValuesMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the values back to their original values when constructed. Used by the dialog
|
||||||
|
* when the user cancels.
|
||||||
|
*/
|
||||||
|
protected void reset() {
|
||||||
|
for (AbstractValue<?> inputValue : valuesMap.values()) {
|
||||||
|
inputValue.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ public class ValuesMapDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelCallback() {
|
protected void cancelCallback() {
|
||||||
|
valuesMap.reset();
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
super.cancelCallback();
|
super.cancelCallback();
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,11 +313,6 @@ public class DomainFileProxy implements DomainFile {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsedExclusivelyBy(Object consumer) {
|
|
||||||
DomainObjectAdapter dobj = getDomainObject();
|
|
||||||
return dobj != null ? dobj.isUsedExclusivelyBy(consumer) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<?> getConsumers() {
|
public ArrayList<?> getConsumers() {
|
||||||
DomainObjectAdapter dobj = getDomainObject();
|
DomainObjectAdapter dobj = getDomainObject();
|
||||||
|
|
|
@ -333,19 +333,12 @@ public abstract class DomainObjectAdapter implements DomainObject {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addConsumer(Object consumer) {
|
public boolean addConsumer(Object consumer) {
|
||||||
if (consumer == null) {
|
Objects.requireNonNull(consumer);
|
||||||
throw new IllegalArgumentException("Consumer must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (consumers) {
|
synchronized (consumers) {
|
||||||
if (isClosed()) {
|
if (isClosed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumers.contains(consumer)) {
|
|
||||||
throw new IllegalArgumentException("Attempted to acquire the " +
|
|
||||||
"domain object more than once by the same consumer: " + consumer);
|
|
||||||
}
|
|
||||||
consumers.add(consumer);
|
consumers.add(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,18 +352,7 @@ public abstract class DomainObjectAdapter implements DomainObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the this file is used only by the given consumer
|
* Returns true if the given consumer is using this object.
|
||||||
* @param consumer the consumer
|
|
||||||
* @return true if the this file is used only by the given consumer
|
|
||||||
*/
|
|
||||||
boolean isUsedExclusivelyBy(Object consumer) {
|
|
||||||
synchronized (consumers) {
|
|
||||||
return (consumers.size() == 1) && (consumers.contains(consumer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given tool is using this object.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isUsedBy(Object consumer) {
|
public boolean isUsedBy(Object consumer) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class AppInfo {
|
||||||
tool = t;
|
tool = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setActiveProject(Project p) {
|
public static void setActiveProject(Project p) {
|
||||||
activeProject = p;
|
activeProject = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ package ghidra.framework.model;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.util.ReadOnlyException;
|
import ghidra.util.ReadOnlyException;
|
||||||
|
@ -216,7 +217,7 @@ public interface DomainObject {
|
||||||
* Returns the list of consumers on this domainObject
|
* Returns the list of consumers on this domainObject
|
||||||
* @return the list of consumers.
|
* @return the list of consumers.
|
||||||
*/
|
*/
|
||||||
public ArrayList<Object> getConsumerList();
|
public List<Object> getConsumerList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given consumer is using (has open) this domain object.
|
* Returns true if the given consumer is using (has open) this domain object.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue