Candidate release of source code.
|
@ -0,0 +1,55 @@
|
|||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the genated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be on table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<tocref id="Code Browser">
|
||||
<tocdef id="Program Differences" sortgroup="e" text="Program Differences" target="help/topics/Diff/Diff.htm" />
|
||||
</tocref>
|
||||
</tocroot>
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
WARNING!
|
||||
This file is copied to all help directories. If you change this file, you must copy it
|
||||
to each src/main/help/help/shared directory.
|
||||
|
||||
|
||||
Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but
|
||||
px (pixel) or with no type marking.
|
||||
|
||||
*/
|
||||
|
||||
body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */
|
||||
li { font-family:times new roman; font-size:14pt; }
|
||||
h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; }
|
||||
h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; }
|
||||
h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; }
|
||||
h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; }
|
||||
|
||||
/*
|
||||
P tag code. Most of the help files nest P tags inside of blockquote tags (the was the
|
||||
way it had been done in the beginning). The net effect is that the text is indented. In
|
||||
modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in
|
||||
blockquote tags, as well as naked P tags. The following two lines accomplish this. Note
|
||||
that the 'blockquote p' definition will inherit from the first 'p' definition.
|
||||
*/
|
||||
p { margin-left: 40px; font-family:times new roman; font-size:14pt; }
|
||||
blockquote p { margin-left: 10px; }
|
||||
|
||||
p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
|
||||
/*
|
||||
We wish for a tables to have space between it and the preceding element, so that text
|
||||
is not too close to the top of the table. Also, nest the table a bit so that it is clear
|
||||
the table relates to the preceding text.
|
||||
*/
|
||||
table { margin-left: 20px; margin-top: 10px; width: 80%;}
|
||||
td { font-family:times new roman; font-size:14pt; vertical-align: top; }
|
||||
th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; }
|
||||
|
||||
code { color: black; font-family: courier new; font-size: 14pt; }
|
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/arrow.gif
Normal file
After Width: | Height: | Size: 69 B |
After Width: | Height: | Size: 859 B |
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/menu16.gif
Normal file
After Width: | Height: | Size: 62 B |
After Width: | Height: | Size: 4.1 KiB |
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/note.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/redo.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/tip.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Ghidra/Features/ProgramDiff/src/main/help/help/shared/undo.png
Normal file
After Width: | Height: | Size: 185 B |
After Width: | Height: | Size: 1.3 KiB |
1335
Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/Diff.htm
Normal file
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 192 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 663 B |
After Width: | Height: | Size: 193 B |
After Width: | Height: | Size: 1,000 B |
|
@ -0,0 +1,158 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* NOTE: This disables auto analysis while differences are applied and restores auto analysis enablement at end.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.dialogs.ReadTextDialog;
|
||||
|
||||
/**
|
||||
* Command to apply diffs to current program.
|
||||
*
|
||||
*/
|
||||
class ApplyDiffCommand extends BackgroundCommand implements AnalysisWorker {
|
||||
|
||||
private AddressSetView p1AddressSet;
|
||||
private DiffController diffControl;
|
||||
private String title;
|
||||
private String applyMsg;
|
||||
private boolean applied;
|
||||
private ProgramDiffPlugin plugin;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
ApplyDiffCommand(ProgramDiffPlugin plugin, AddressSetView program1AddressSet,
|
||||
DiffController diffControl) {
|
||||
super("Apply Differences", false, true, true);
|
||||
this.plugin = plugin;
|
||||
this.p1AddressSet = program1AddressSet;
|
||||
this.diffControl = diffControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor)
|
||||
throws Exception, CancelledException {
|
||||
// Diff apply done with analysis disabled
|
||||
return diffControl.apply(p1AddressSet, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkerName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.cmd.BackgroundCommand#applyTo(ghidra.framework.model.DomainObject, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
monitor.setMessage("ApplyDiffTask starting...");
|
||||
applied = false;
|
||||
final ProgramLocation origLocation = plugin.getProgramLocation();
|
||||
if (!plugin.isTaskInProgress()) {
|
||||
|
||||
plugin.setTaskInProgress(true);
|
||||
String statusMsg = "One or more differences couldn't be applied.";
|
||||
title = "Program Diff: One or more differences couldn't be applied.";
|
||||
applyMsg = null;
|
||||
setStatusMsg(null);
|
||||
try {
|
||||
AutoAnalysisManager autoAnalysisManager =
|
||||
AutoAnalysisManager.getAnalysisManager(plugin.getFirstProgram());
|
||||
boolean merged = autoAnalysisManager.scheduleWorker(this, null, false, monitor);
|
||||
if (merged) {
|
||||
statusMsg =
|
||||
"Apply differences has finished."
|
||||
+ " If your expected change didn't occur, check your Diff Apply Settings.";
|
||||
title = "Program Diff: Apply differences has finished.";
|
||||
applied = true;
|
||||
}
|
||||
else {
|
||||
applyMsg = diffControl.getApplyMessage();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
applyMsg = "Unexpected InterruptedException\n" + diffControl.getApplyMessage();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
String message = "";
|
||||
// Protect against dereferencing the getCause call above, which may return null.
|
||||
if (t != null) {
|
||||
String excMessage = t.getMessage();
|
||||
if (excMessage != null && excMessage.length() > 0) {
|
||||
message = excMessage + "\n";
|
||||
}
|
||||
}
|
||||
Msg.showError(this, plugin.getListingPanel(), "Error Applying Diff",
|
||||
"An error occured while applying differences.\n"
|
||||
+ "Only some of the differences may have been applied.",
|
||||
(t != null) ? t : e);
|
||||
applyMsg = message + diffControl.getApplyMessage();
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
statusMsg =
|
||||
"User cancelled \"Apply Differences\". "
|
||||
+ "Differences were only partially applied.";
|
||||
applyMsg = diffControl.getApplyMessage();
|
||||
}
|
||||
finally {
|
||||
setStatusMsg(statusMsg);
|
||||
plugin.getTool().setStatusInfo(statusMsg);
|
||||
plugin.setTaskInProgress(false);
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
plugin.adjustDiffDisplay();
|
||||
plugin.firePluginEvent(new ProgramSelectionPluginEvent(plugin.getName(),
|
||||
plugin.getCurrentSelection(), plugin.getCurrentProgram()));
|
||||
plugin.programLocationChanged(origLocation, null);
|
||||
if (applyMsg != null && applyMsg.length() > 0) {
|
||||
ReadTextDialog detailsDialog = new ReadTextDialog(title, applyMsg);
|
||||
plugin.getTool().showDialog(detailsDialog, plugin.getListingPanel());
|
||||
}
|
||||
}
|
||||
};
|
||||
// // The events were disabled while doing apply Diff. Now re-enable them by firing object restored event.
|
||||
// ((DomainObjectAdapter)currentProgram).fireEvent(new DomainObjectChangeRecord(
|
||||
// DomainObject.DO_OBJECT_RESTORED));
|
||||
// ((DomainObjectAdapter)currentProgram).flushEvents();
|
||||
if (!monitor.isCancelled()) {
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import ghidra.framework.model.DomainObjectException;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.ClosedException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Task that generates the address set containing the differences between
|
||||
* two programs.
|
||||
*
|
||||
*/
|
||||
class CreateDiffTask extends Task {
|
||||
private ProgramDiffPlugin plugin;
|
||||
private Program program1;
|
||||
private Program program2;
|
||||
private AddressSetView limitedAddressSet;
|
||||
private ProgramDiffFilter diffFilter;
|
||||
private ProgramMergeFilter applyFilter;
|
||||
private DiffApplySettingsProvider diffApplySettingsProvider;
|
||||
private boolean isLimitedToSelection;
|
||||
|
||||
/**
|
||||
* Construct new LoadDiffTask that loads the dialog with the two
|
||||
* programs and indicates their differences. The differences should be
|
||||
* restricted to the limitedAddressSet.
|
||||
*
|
||||
*/
|
||||
CreateDiffTask(ProgramDiffPlugin plugin, Program program1, Program program2,
|
||||
AddressSetView limitedAddressSet, boolean isLimitedToSelection,
|
||||
ProgramDiffFilter diffFilter, ProgramMergeFilter applyFilter) {
|
||||
super("Checking Program Differences", true, false, false);
|
||||
this.plugin = plugin;
|
||||
this.program1 = program1;
|
||||
this.program2 = program2;
|
||||
this.limitedAddressSet = limitedAddressSet;
|
||||
this.isLimitedToSelection = isLimitedToSelection;
|
||||
this.diffFilter = diffFilter;
|
||||
this.applyFilter = applyFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the method TaskMonitor called to do the work.
|
||||
*
|
||||
* @param monitor The TaskMonitor that will monitor the executing Task. Will be null if
|
||||
* this task declared that it does not use a TaskMonitor
|
||||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
if (plugin.isTaskInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DiffController dc = null;
|
||||
plugin.setTaskInProgress(true);
|
||||
monitor.setIndeterminate(true);
|
||||
monitor.setMessage("Checking Program Differences");
|
||||
try {
|
||||
dc = new DiffController(program1, program2, limitedAddressSet, this.diffFilter,
|
||||
this.applyFilter, monitor);
|
||||
AddressSetView filteredDifferences = dc.getFilteredDifferences(monitor);
|
||||
boolean noFilteredDifferencesFound = filteredDifferences.isEmpty();
|
||||
plugin.setDiffController(dc);
|
||||
dc.differencesChanged(monitor);
|
||||
dc.setLocation(plugin.getCurrentAddress());
|
||||
monitor.setMessage("Done");
|
||||
Runnable r = () -> displayDifferencesMessageIfNecessary(noFilteredDifferencesFound);
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
catch (DomainObjectException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ClosedException) {
|
||||
// this can happen if you close the tool while this task is calculating diffs
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch (ProgramConflictException e) {
|
||||
showErrorMessage(e.getMessage());
|
||||
return;
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
plugin.setDiffController(dc);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
completed();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayDifferencesMessageIfNecessary(final boolean noFilteredDifferencesFound) {
|
||||
|
||||
try {
|
||||
ProgramMemoryComparator programMemoryComparator =
|
||||
new ProgramMemoryComparator(program1, program2);
|
||||
boolean hasMemoryDifferences = programMemoryComparator.hasMemoryDifferences();
|
||||
|
||||
String title = null;
|
||||
String message = null;
|
||||
|
||||
if (isLimitedToSelection) {
|
||||
if (noFilteredDifferencesFound) {
|
||||
title = "No Differences In Selection";
|
||||
message = "No differences were found for the addresses in the selection" +
|
||||
"\nand for the types of program information being compared by this Diff.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasMemoryDifferences) {
|
||||
title = "Memory Differs";
|
||||
message = getMemoryDifferenceMessage(noFilteredDifferencesFound,
|
||||
programMemoryComparator);
|
||||
}
|
||||
else if (noFilteredDifferencesFound) {
|
||||
// Not a Diff on a selection, memory is the same, and no differences found
|
||||
// for current filter and compatible addresses.
|
||||
title = "No Differences";
|
||||
message =
|
||||
"No differences were found for the addresses that are compatible between the two" +
|
||||
"\nprograms for the types of program information being compared by this Diff.";
|
||||
}
|
||||
}
|
||||
if (title != null) {
|
||||
String note =
|
||||
"\n \nNote: Some parts of the program are not handled by Diff (for example:" +
|
||||
"\n Markup where only one program has that memory address," +
|
||||
"\n Registers that are not common to both programs' languages," +
|
||||
"\n Program Trees, Data Types that haven't been applied to memory, etc.)";
|
||||
|
||||
Msg.showInfo(getClass(), plugin.getListingPanel(), title, message + note);
|
||||
}
|
||||
}
|
||||
catch (ProgramConflictException e) {
|
||||
Msg.showError(getClass(), plugin.getListingPanel(), "Can't Compare Memory",
|
||||
"Diff can't compare the two programs memory. " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String getMemoryDifferenceMessage(final boolean noFilteredDifferencesFound,
|
||||
ProgramMemoryComparator programMemoryComparator) {
|
||||
String message;
|
||||
message = "The memory addresses defined by the two programs are not the same.\n \n" +
|
||||
(noFilteredDifferencesFound ? "However, no differences were found "
|
||||
: "Differences are highlighted ") +
|
||||
"for the addresses that are compatible between" +
|
||||
"\nthe two programs for the types of program information being compared by this Diff.";
|
||||
|
||||
AddressSet addressesOnlyInOne = programMemoryComparator.getAddressesOnlyInOne();
|
||||
if (!addressesOnlyInOne.isEmpty()) {
|
||||
message +=
|
||||
"\n \nSome addresses are only in program 1 : " + addressesOnlyInOne.toString();
|
||||
}
|
||||
|
||||
AddressSet addressesOnlyInTwo = programMemoryComparator.getAddressesOnlyInTwo();
|
||||
if (!addressesOnlyInTwo.isEmpty()) {
|
||||
message +=
|
||||
"\n \nSome addresses are only in program 2 : " + addressesOnlyInTwo.toString();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private void showErrorMessage(String message) {
|
||||
SystemUtilities.runSwingLater(() -> Msg.showError(getClass(),
|
||||
plugin.getTool().getToolFrame(), "Can't Perform Diff", message));
|
||||
}
|
||||
|
||||
private void completed() {
|
||||
if (plugin.isDisposed()) {
|
||||
// the tool was closed while this task was running
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getCurrentProgram() == null) {
|
||||
// the program was closed while this task was running
|
||||
return;
|
||||
}
|
||||
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
diffApplySettingsProvider = plugin.getDiffApplySettingsProvider();
|
||||
diffApplySettingsProvider.configure(applyFilter);
|
||||
plugin.adjustDiffDisplay();
|
||||
});
|
||||
|
||||
plugin.setTaskInProgress(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.codebrowser.OtherPanelContext;
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.util.ToolConstants;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Creates the actions for the program diff plugin.
|
||||
*/
|
||||
class DiffActionManager {
|
||||
|
||||
private static final String SELECT_GROUP = "Select";
|
||||
private static final String GET_DIFF_GROUP = "GetDiff";
|
||||
private static final String DIFF_INFO_GROUP = "DiffInfo";
|
||||
private static final String DIFF_NAVIGATE_GROUP = "DiffNavigate";
|
||||
private static final String TOGGLE_VIEW_ICON_NAME = "images/table_relationship.png";
|
||||
private static final String GROUP = "Diff";
|
||||
private ProgramDiffPlugin plugin;
|
||||
private CodeViewerService codeViewerService;
|
||||
|
||||
static final String APPLY_DIFFS_ACTION = "Apply Differences";
|
||||
static final String APPLY_DIFFS_NEXT_ACTION = "Apply Differences and Goto Next Difference";
|
||||
static final String IGNORE_DIFFS_NEXT_ACTION = "Ignore Selection and Goto Next Difference";
|
||||
static final String NEXT_DIFF_ACTION = "Next Difference";
|
||||
static final String PREVIOUS_DIFF_ACTION = "Previous Difference";
|
||||
static final String DIFF_DETAILS_ACTION = "Diff Location Details";
|
||||
static final String SHOW_DIFF_SETTINGS_ACTION = "Show Diff Apply Settings";
|
||||
static final String GET_DIFFS_ACTION = "Get Differences";
|
||||
static final String SELECT_ALL_DIFFS_ACTION = "Select All Differences";
|
||||
static final String P1_SELECT_TO_P2_ACTION = "Set Program1 Selection On Program2";
|
||||
static final String OPEN_CLOSE_PROGRAM2_ACTION = "Open/Close Program View";
|
||||
static final String VIEW_PROGRAM_DIFF_ACTION = "View Program Differences";
|
||||
|
||||
private DockingAction applyDiffsAction;
|
||||
private DockingAction applyDiffsNextAction;
|
||||
private DockingAction ignoreDiffsAction;
|
||||
private DockingAction nextDiffAction;
|
||||
private DockingAction previousDiffAction;
|
||||
private DockingAction diffDetailsAction;
|
||||
private DockingAction showDiffSettingsAction;
|
||||
private DockingAction getDiffsAction;
|
||||
private DockingAction selectAllDiffsAction;
|
||||
private DockingAction p1SelectToP2Action;
|
||||
private ToggleDockingAction openCloseProgram2Action;
|
||||
private DockingAction viewProgramDiffAction;
|
||||
|
||||
/**
|
||||
* Creates an action manager for the Program Diff plugin.
|
||||
*/
|
||||
public DiffActionManager(ProgramDiffPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
createActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the code viewer service that the Program Diff will use for setting local actions.
|
||||
* @param codeViewerService the code viewer service
|
||||
*/
|
||||
void setCodeViewerService(CodeViewerService codeViewerService) {
|
||||
this.codeViewerService = codeViewerService;
|
||||
codeViewerService.addLocalAction(openCloseProgram2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Program Diff's local actions to the code viewer.
|
||||
*/
|
||||
void addActions() {
|
||||
codeViewerService.addLocalAction(applyDiffsAction);
|
||||
codeViewerService.addLocalAction(applyDiffsNextAction);
|
||||
codeViewerService.addLocalAction(ignoreDiffsAction);
|
||||
codeViewerService.addLocalAction(diffDetailsAction);
|
||||
codeViewerService.addLocalAction(nextDiffAction);
|
||||
codeViewerService.addLocalAction(previousDiffAction);
|
||||
codeViewerService.addLocalAction(showDiffSettingsAction);
|
||||
codeViewerService.addLocalAction(getDiffsAction);
|
||||
codeViewerService.addLocalAction(selectAllDiffsAction);
|
||||
codeViewerService.addLocalAction(p1SelectToP2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Program Diff's local actions from the code viewer.
|
||||
*/
|
||||
void removeActions() {
|
||||
codeViewerService.removeLocalAction(applyDiffsAction);
|
||||
codeViewerService.removeLocalAction(applyDiffsNextAction);
|
||||
codeViewerService.removeLocalAction(ignoreDiffsAction);
|
||||
codeViewerService.removeLocalAction(diffDetailsAction);
|
||||
codeViewerService.removeLocalAction(nextDiffAction);
|
||||
codeViewerService.removeLocalAction(previousDiffAction);
|
||||
codeViewerService.removeLocalAction(showDiffSettingsAction);
|
||||
codeViewerService.removeLocalAction(getDiffsAction);
|
||||
codeViewerService.removeLocalAction(selectAllDiffsAction);
|
||||
codeViewerService.removeLocalAction(p1SelectToP2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to adjust the Program Diff's actions when a program is closed.
|
||||
* @param program the closed program
|
||||
*/
|
||||
void programClosed(Program program) {
|
||||
boolean hasProgram = (plugin.getCurrentProgram() != null);
|
||||
openCloseProgram2Action.setEnabled(hasProgram && !plugin.isTaskInProgress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to adjust or add/remove the Program Diff's actions when a program
|
||||
* becomes the active program.
|
||||
* @param program the newly active program
|
||||
*/
|
||||
void setActiveProgram(Program program) {
|
||||
viewProgramDiffAction.setEnabled(program != null);
|
||||
|
||||
boolean enabled = program != null && !plugin.isTaskInProgress();
|
||||
openCloseProgram2Action.setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
if (openCloseProgram2Action.isSelected()) {
|
||||
// we are diffing--is the current program the diff program?
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
if (firstProgram == program) {
|
||||
openCloseProgram2Action.setDescription("Close Diff View");
|
||||
}
|
||||
else {
|
||||
openCloseProgram2Action.setDescription("Bring Diff View to Front");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no active diff
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification to the action manager that a program was opened as the
|
||||
* second program to the program Diff. Actions are adjusted accordingly.
|
||||
*/
|
||||
void secondProgramOpened() {
|
||||
openCloseProgram2Action.setSelected(true);
|
||||
openCloseProgram2Action.setDescription("Close Diff View");
|
||||
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
Program secondProgram = plugin.getSecondProgram();
|
||||
String firstName = firstProgram.getName();
|
||||
String secondName = secondProgram.getName();
|
||||
|
||||
//@formatter:off
|
||||
openCloseProgram2Action.setDescription("<html><center>Close Diff View</center><br>" +
|
||||
"Current diff: " +
|
||||
"<b>"+firstName+"</b> to <b>" +secondName+"</b>");
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification to the action manager that the second program to the
|
||||
* program Diff was closed. Actions are adjusted accordingly.
|
||||
*/
|
||||
void secondProgramClosed() {
|
||||
openCloseProgram2Action.setSelected(false);
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
showDiffSettingsAction.setEnabled(false);
|
||||
diffDetailsAction.setEnabled(false);
|
||||
removeActions();
|
||||
}
|
||||
|
||||
void setP1SelectToP2ActionEnabled(boolean enabled) {
|
||||
p1SelectToP2Action.setEnabled(enabled);
|
||||
}
|
||||
|
||||
void setOpenCloseActionSelected(boolean selected) {
|
||||
openCloseProgram2Action.setSelected(selected);
|
||||
}
|
||||
|
||||
void updateActions(boolean taskInProgress, boolean inDiff, boolean hasSelectionInView,
|
||||
boolean applyFilterIsSet, boolean hasProgram2, boolean hasHighlights) {
|
||||
DiffController diffControl = plugin.getDiffController();
|
||||
applyDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
|
||||
applyDiffsNextAction.setEnabled(
|
||||
!taskInProgress && inDiff && hasSelectionInView && diffControl.hasNext());
|
||||
ignoreDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
|
||||
nextDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasNext());
|
||||
previousDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasPrevious());
|
||||
diffDetailsAction.setEnabled(!taskInProgress && hasProgram2);
|
||||
showDiffSettingsAction.setEnabled(!taskInProgress && inDiff);
|
||||
getDiffsAction.setEnabled(!taskInProgress && hasProgram2);
|
||||
selectAllDiffsAction.setEnabled(!taskInProgress && !inDiff || hasHighlights);
|
||||
p1SelectToP2Action.setEnabled(hasProgram2 && !plugin.getCurrentSelection().isEmpty());
|
||||
|
||||
Program currentProgram = plugin.getCurrentProgram();
|
||||
boolean hasProgram = (currentProgram != null);
|
||||
openCloseProgram2Action.setEnabled(hasProgram && !taskInProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the actions.
|
||||
*/
|
||||
void dispose() {
|
||||
codeViewerService.removeLocalAction(openCloseProgram2Action);
|
||||
plugin.getTool().removeAction(viewProgramDiffAction);
|
||||
removeActions();
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
viewProgramDiffAction = new DockingAction(VIEW_PROGRAM_DIFF_ACTION, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.selectProgram2();
|
||||
}
|
||||
};
|
||||
String[] menuPath = { ToolConstants.MENU_TOOLS, "Program &Differences..." };
|
||||
viewProgramDiffAction.setEnabled(plugin.getCurrentProgram() != null);
|
||||
viewProgramDiffAction.setMenuBarData(new MenuData(menuPath, "View"));
|
||||
viewProgramDiffAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_2, 0));
|
||||
viewProgramDiffAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, "Program_Differences"));
|
||||
plugin.getTool().addAction(viewProgramDiffAction);
|
||||
|
||||
applyDiffsAction = new DiffAction(APPLY_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.applyDiff();
|
||||
}
|
||||
};
|
||||
ImageIcon icon = ResourceManager.loadImage("images/pencil16.png");
|
||||
applyDiffsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F3, 0));
|
||||
applyDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Apply Selection" }, icon, GROUP));
|
||||
applyDiffsAction.setDescription(
|
||||
"Applies the differences from the second program's selection using the settings.");
|
||||
applyDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
|
||||
applyDiffsNextAction = new DiffAction(APPLY_DIFFS_NEXT_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.applyDiffAndGoNext();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/pencil_arrow16.png");
|
||||
String[] applyDiffsPath = { "Apply Selection and Goto Next Difference" };
|
||||
applyDiffsNextAction.setPopupMenuData(new MenuData(applyDiffsPath, icon, GROUP));
|
||||
applyDiffsNextAction.setKeyBindingData(
|
||||
new KeyBindingData(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK));
|
||||
applyDiffsNextAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
applyDiffsNextAction.setDescription(
|
||||
"Applies the differences from the second program's selection using the settings. " +
|
||||
"Then moves the cursor to the next difference.");
|
||||
|
||||
ignoreDiffsAction = new DiffAction(IGNORE_DIFFS_NEXT_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.ignoreDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/eraser_arrow16.png");
|
||||
ignoreDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Ignore Selection and Goto Next Difference" }, icon,
|
||||
GROUP));
|
||||
ignoreDiffsAction.setDescription(
|
||||
"Ignores the selected program differences and moves the cursor to the next difference.");
|
||||
ignoreDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
|
||||
nextDiffAction = new DiffAction(NEXT_DIFF_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.nextDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/down.png");
|
||||
nextDiffAction.setKeyBindingData(
|
||||
new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
nextDiffAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Next Difference" }, icon, DIFF_NAVIGATE_GROUP));
|
||||
nextDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
|
||||
nextDiffAction.setDescription("Go to the next highlighted difference.");
|
||||
|
||||
previousDiffAction = new DiffAction(PREVIOUS_DIFF_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.previousDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/up.png");
|
||||
previousDiffAction.setKeyBindingData(
|
||||
new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
previousDiffAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Previous Difference" }, icon, DIFF_NAVIGATE_GROUP));
|
||||
previousDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
|
||||
previousDiffAction.setDescription("Go to previous highlighted difference.");
|
||||
|
||||
diffDetailsAction = new DiffAction(DIFF_DETAILS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.showDiffDetails();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/xmag.png");
|
||||
diffDetailsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F5, 0));
|
||||
diffDetailsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Location Details..." }, icon, DIFF_INFO_GROUP));
|
||||
diffDetailsAction.setToolBarData(new ToolBarData(icon, DIFF_INFO_GROUP));
|
||||
diffDetailsAction.setDescription(
|
||||
"Show details of the program differences at the current location.");
|
||||
|
||||
showDiffSettingsAction = new DiffAction(SHOW_DIFF_SETTINGS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.showDiffApplySettings();
|
||||
}
|
||||
};
|
||||
showDiffSettingsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Show Diff Apply Settings..." }, GET_DIFF_GROUP));
|
||||
showDiffSettingsAction.setDescription(
|
||||
"Displays the current program difference apply settings.");
|
||||
showDiffSettingsAction.setToolBarData(
|
||||
new ToolBarData(DiffApplySettingsProvider.ICON, GET_DIFF_GROUP));
|
||||
|
||||
getDiffsAction = new DiffAction(GET_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.diff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/Diff16.png");
|
||||
getDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Get Differences..." }, icon, GET_DIFF_GROUP));
|
||||
getDiffsAction.setToolBarData(new ToolBarData(icon, GET_DIFF_GROUP));
|
||||
getDiffsAction.setDescription("Determines program differences.");
|
||||
|
||||
selectAllDiffsAction = new DiffAction(SELECT_ALL_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.selectAllDiffs();
|
||||
}
|
||||
};
|
||||
selectAllDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Select All Differences" }, SELECT_GROUP));
|
||||
selectAllDiffsAction.setDescription(
|
||||
"Selects all highlighted differences in the second program.");
|
||||
|
||||
p1SelectToP2Action = new DiffAction(P1_SELECT_TO_P2_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.setP1SelectionOnP2();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/DiffSelect16.png");
|
||||
p1SelectToP2Action.setDescription(
|
||||
"Select Program 2 highlights using selection in Program 1.");
|
||||
p1SelectToP2Action.setToolBarData(new ToolBarData(icon, SELECT_GROUP));
|
||||
|
||||
openCloseProgram2Action =
|
||||
new ToggleDockingAction(OPEN_CLOSE_PROGRAM2_ACTION, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
//
|
||||
// No active diff--start one.
|
||||
//
|
||||
if (openCloseProgram2Action.isSelected()) {
|
||||
plugin.selectProgram2();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// We have a diff session--is the current program the one for the diff session?
|
||||
// If not, simply make the diff program the active program (this is useful for
|
||||
// users when they were diffing, but then opened a different program).
|
||||
//
|
||||
if (activateDiffProgram()) {
|
||||
// clicking the action will change the selected state--keep it selected
|
||||
setSelected(true);
|
||||
setDescription("Close Diff View");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Otherwise, close the diff.
|
||||
//
|
||||
closeDiff();
|
||||
}
|
||||
|
||||
private void closeDiff() {
|
||||
int choice = OptionDialog.showYesNoCancelDialog(null, "Close Diff Session",
|
||||
"Close the current diff session?");
|
||||
if (choice == OptionDialog.YES_OPTION) {
|
||||
plugin.closeProgram2();
|
||||
setDescription("Open Diff View");
|
||||
}
|
||||
else {
|
||||
// clicking the action will change the selected state--keep it selected
|
||||
setSelected(true);
|
||||
setDescription("Close Diff View");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean activateDiffProgram() {
|
||||
|
||||
Program currentProgram = plugin.getCurrentProgram();
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
boolean isFirstProgram = (firstProgram == currentProgram);
|
||||
if (isFirstProgram) {
|
||||
return false; // already active--nothing to do!
|
||||
}
|
||||
|
||||
plugin.activeProgram(firstProgram);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage(TOGGLE_VIEW_ICON_NAME);
|
||||
openCloseProgram2Action.setEnabled(false);
|
||||
openCloseProgram2Action.setKeyBindingData(
|
||||
new KeyBindingData('C', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
openCloseProgram2Action.setToolBarData(new ToolBarData(icon, "zzz"));
|
||||
openCloseProgram2Action.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, OPEN_CLOSE_PROGRAM2_ACTION));
|
||||
openCloseProgram2Action.setSelected(false);
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
}
|
||||
|
||||
private abstract class DiffAction extends DockingAction {
|
||||
DiffAction(String name) {
|
||||
super(name, plugin.getName());
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DIFF, name));
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return (context instanceof OtherPanelContext);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,544 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Manages the options for the Diff apply settings.
|
||||
*/
|
||||
class DiffApplySettingsOptionManager {
|
||||
|
||||
public static final String DIFF_OPTIONS = "Diff";
|
||||
public static final String DIFF_APPLY_SETTINGS_OPTIONS = "Default Apply Settings";
|
||||
|
||||
private static final int PROGRAM_CONTEXT = 1 << 0;
|
||||
private static final int BYTES = 1 << 1;
|
||||
private static final int CODE_UNITS = 1 << 2;
|
||||
private static final int REFERENCES = 1 << 3;
|
||||
private static final int PLATE_COMMENTS = 1 << 4;
|
||||
private static final int PRE_COMMENTS = 1 << 5;
|
||||
private static final int EOL_COMMENTS = 1 << 6;
|
||||
private static final int REPEATABLE_COMMENTS = 1 << 7;
|
||||
private static final int POST_COMMENTS = 1 << 8;
|
||||
private static final int SYMBOLS = 1 << 9;
|
||||
private static final int BOOKMARKS = 1 << 10;
|
||||
private static final int PROPERTIES = 1 << 11;
|
||||
private static final int FUNCTIONS = 1 << 12;
|
||||
private static final int FUNCTION_TAGS = 1 << 13;
|
||||
|
||||
private static final String OPTION_PROGRAM_CONTEXT = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Program Context";
|
||||
private static final String OPTION_BYTES = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
|
||||
"Bytes";
|
||||
private static final String OPTION_CODE_UNITS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Code Units";
|
||||
private static final String OPTION_REFERENCES = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "References";
|
||||
private static final String OPTION_PLATE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Plate Comments";
|
||||
private static final String OPTION_PRE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Pre Comments";
|
||||
private static final String OPTION_EOL_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "End Of Line Comments";
|
||||
private static final String OPTION_REPEATABLE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Repeatable Comments";
|
||||
private static final String OPTION_POST_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Post Comments";
|
||||
private static final String OPTION_SYMBOLS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
|
||||
"Labels";
|
||||
private static final String OPTION_BOOKMARKS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
|
||||
"Bookmarks";
|
||||
private static final String OPTION_PROPERTIES = DIFF_APPLY_SETTINGS_OPTIONS +
|
||||
Options.DELIMITER + "Properties";
|
||||
private static final String OPTION_FUNCTIONS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
|
||||
"Functions";
|
||||
private static final String OPTION_FUNCTION_TAGS =
|
||||
DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER + "Function Tags";
|
||||
|
||||
// public static final String MERGE = "Merge";
|
||||
// public static final String MERGE_SYMBOLS_1 = "Merge";
|
||||
// public static final String MERGE_SYMBOLS_2 = "Merge & Set Primary";
|
||||
|
||||
public static enum REPLACE_CHOICE {
|
||||
IGNORE("Ignore"), REPLACE("Replace");
|
||||
private String description;
|
||||
|
||||
REPLACE_CHOICE(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum MERGE_CHOICE {
|
||||
IGNORE("Ignore"), REPLACE("Replace"), MERGE("Merge");
|
||||
private String description;
|
||||
|
||||
MERGE_CHOICE(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
// Special choice for symbols in that if merged, should the primary attribute be set.
|
||||
public static enum SYMBOL_MERGE_CHOICE {
|
||||
IGNORE("Ignore"),
|
||||
REPLACE("Replace"),
|
||||
MERGE_DONT_SET_PRIMARY("Merge"),
|
||||
MERGE_AND_SET_PRIMARY("Merge & Set Primary");
|
||||
private String description;
|
||||
|
||||
SYMBOL_MERGE_CHOICE(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
private Plugin plugin;
|
||||
private HelpLocation help;
|
||||
private String HELP_TOPIC = "Diff";
|
||||
|
||||
/**
|
||||
* Creates a new option manager for Diff apply settings.
|
||||
* @param plugin the plugin that owns this options manager.
|
||||
*/
|
||||
DiffApplySettingsOptionManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes class variables for this option manager and gets the default options.
|
||||
*/
|
||||
private void init() {
|
||||
|
||||
help = new HelpLocation(HELP_TOPIC, "DiffApplySettingsToolOptions");
|
||||
Options options = plugin.getTool().getOptions(DIFF_OPTIONS);
|
||||
options.setOptionsHelpLocation(help);
|
||||
|
||||
// Set the help strings
|
||||
options.registerOption(
|
||||
OPTION_PROGRAM_CONTEXT,
|
||||
REPLACE_CHOICE.REPLACE,
|
||||
help,
|
||||
getReplaceDescription("program context register value",
|
||||
"program context register values"));
|
||||
options.registerOption(OPTION_BYTES, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("byte", "bytes"));
|
||||
|
||||
options.registerOption(OPTION_CODE_UNITS, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("code unit", "code units"));
|
||||
|
||||
options.registerOption(OPTION_REFERENCES, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("reference", "references"));
|
||||
options.registerOption(OPTION_PLATE_COMMENTS, MERGE_CHOICE.MERGE, help,
|
||||
getMergeDescription("plate comment", "plate comments"));
|
||||
options.registerOption(OPTION_PRE_COMMENTS, MERGE_CHOICE.MERGE, help,
|
||||
getMergeDescription("pre-comment", "pre-comments"));
|
||||
options.registerOption(OPTION_EOL_COMMENTS, MERGE_CHOICE.MERGE, help,
|
||||
getMergeDescription("end of line comment", "end of line comments"));
|
||||
options.registerOption(OPTION_REPEATABLE_COMMENTS, MERGE_CHOICE.MERGE, help,
|
||||
getMergeDescription("repeatable comment", "repeatable comments"));
|
||||
options.registerOption(OPTION_POST_COMMENTS, MERGE_CHOICE.MERGE, help,
|
||||
getMergeDescription("post comment", "post comments"));
|
||||
options.registerOption(OPTION_SYMBOLS, SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY, help,
|
||||
getSymbolDescription());
|
||||
options.registerOption(OPTION_BOOKMARKS, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("bookmark", "bookmarks"));
|
||||
options.registerOption(OPTION_PROPERTIES, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("property", "properties"));
|
||||
options.registerOption(OPTION_FUNCTIONS, REPLACE_CHOICE.REPLACE, help,
|
||||
getReplaceDescription("function", "functions"));
|
||||
options.registerOption(OPTION_FUNCTION_TAGS, MERGE_CHOICE.MERGE, help,
|
||||
getReplaceDescription("function tag", "function tags"));
|
||||
|
||||
getDefaultApplyFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the merge filter for the default apply settings.
|
||||
* @return merge filter indicating default apply settings.
|
||||
*/
|
||||
public ProgramMergeFilter getDefaultApplyFilter() {
|
||||
PluginTool tool = plugin.getTool();
|
||||
Options options = tool.getOptions(DIFF_OPTIONS);
|
||||
|
||||
// case PROGRAM_CONTEXT:
|
||||
// case BYTES:
|
||||
// case CODE_UNITS:
|
||||
// case REFERENCES:
|
||||
// case BOOKMARKS:
|
||||
// case PROPERTIES:
|
||||
// case FUNCTIONS:
|
||||
|
||||
// Get the current settings as options
|
||||
REPLACE_CHOICE programContext =
|
||||
options.getEnum(OPTION_PROGRAM_CONTEXT, REPLACE_CHOICE.REPLACE);
|
||||
REPLACE_CHOICE bytes = options.getEnum(OPTION_BYTES, REPLACE_CHOICE.REPLACE);
|
||||
REPLACE_CHOICE codeUnits = options.getEnum(OPTION_CODE_UNITS, REPLACE_CHOICE.REPLACE);
|
||||
REPLACE_CHOICE references = options.getEnum(OPTION_REFERENCES, REPLACE_CHOICE.REPLACE);
|
||||
MERGE_CHOICE plateComments = options.getEnum(OPTION_PLATE_COMMENTS, MERGE_CHOICE.MERGE);
|
||||
MERGE_CHOICE preComments = options.getEnum(OPTION_PRE_COMMENTS, MERGE_CHOICE.MERGE);
|
||||
MERGE_CHOICE eolComments = options.getEnum(OPTION_EOL_COMMENTS, MERGE_CHOICE.MERGE);
|
||||
MERGE_CHOICE repeatableComments =
|
||||
options.getEnum(OPTION_REPEATABLE_COMMENTS, MERGE_CHOICE.MERGE);
|
||||
MERGE_CHOICE postComments = options.getEnum(OPTION_POST_COMMENTS, MERGE_CHOICE.MERGE);
|
||||
SYMBOL_MERGE_CHOICE symbols =
|
||||
options.getEnum(OPTION_SYMBOLS, SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY);
|
||||
REPLACE_CHOICE bookmarks = options.getEnum(OPTION_BOOKMARKS, REPLACE_CHOICE.REPLACE);
|
||||
REPLACE_CHOICE properties = options.getEnum(OPTION_PROPERTIES, REPLACE_CHOICE.REPLACE);
|
||||
REPLACE_CHOICE functions = options.getEnum(OPTION_FUNCTIONS, REPLACE_CHOICE.REPLACE);
|
||||
MERGE_CHOICE functionTags = options.getEnum(OPTION_FUNCTION_TAGS, MERGE_CHOICE.MERGE);
|
||||
|
||||
// Convert the options to a merge filter.
|
||||
ProgramMergeFilter filter = new ProgramMergeFilter();
|
||||
filter.setFilter(ProgramMergeFilter.PROGRAM_CONTEXT, programContext.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.BYTES, bytes.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.CODE_UNITS, codeUnits.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.REFERENCES, references.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.PLATE_COMMENTS, plateComments.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.PRE_COMMENTS, preComments.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.EOL_COMMENTS, eolComments.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.REPEATABLE_COMMENTS, repeatableComments.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.POST_COMMENTS, postComments.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.SYMBOLS,
|
||||
convertSymbolMergeChoiceToMergeChoice(symbols).ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.BOOKMARKS, bookmarks.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.PROPERTIES, properties.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.FUNCTIONS, functions.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.FUNCTION_TAGS, functionTags.ordinal());
|
||||
filter.setFilter(ProgramMergeFilter.PRIMARY_SYMBOL,
|
||||
convertSymbolMergeChoiceToReplaceChoiceForPrimay(symbols).ordinal());
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private String getReplaceDescription(String settingName, String pluralName) {
|
||||
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
|
||||
getReplaceDescription(pluralName);
|
||||
}
|
||||
|
||||
private String getMergeDescription(String settingName, String pluralName) {
|
||||
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
|
||||
getReplaceDescription(pluralName) + "\n" + getMergeDescription(pluralName);
|
||||
}
|
||||
|
||||
private String getBaseDescription(String settingName) {
|
||||
return "The default Diff setting for applying " + settingName + " differences.";
|
||||
}
|
||||
|
||||
private String getIgnoreDescription(String settingName) {
|
||||
return " Ignore - Don't apply " + settingName + " differences.";
|
||||
}
|
||||
|
||||
private String getReplaceDescription(String pluralName) {
|
||||
return " Replace - Replace " + pluralName + " in the program with those from program 2.";
|
||||
}
|
||||
|
||||
private String getMergeDescription(String pluralName) {
|
||||
return " Merge - Merge " + pluralName + " from program 2 into the current program.";
|
||||
}
|
||||
|
||||
private String getSymbolDescription() {
|
||||
String settingName = "label";
|
||||
String pluralName = "labels";
|
||||
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
|
||||
getReplaceDescription(pluralName) + "\n" + " " + "Merge" + " - Merge " + pluralName +
|
||||
" from program 2 into the current program and don't change which " + settingName +
|
||||
" is primary.\n" + " " + "Merge & Set Primary" + " - Merge " + pluralName +
|
||||
" from program 2 into the current program and set the primary " + settingName +
|
||||
" as it is in program 2, if possible.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the indicated merge filter as the default apply settings.
|
||||
* @param defaultApplyFilter merge filter indicating default apply settings.
|
||||
*/
|
||||
public void saveDefaultApplyFilter(ProgramMergeFilter newDefaultApplyFilter) {
|
||||
PluginTool tool = plugin.getTool();
|
||||
Options options = tool.getOptions(DIFF_OPTIONS);
|
||||
|
||||
saveReplaceOption(options, newDefaultApplyFilter, PROGRAM_CONTEXT);
|
||||
saveReplaceOption(options, newDefaultApplyFilter, BYTES);
|
||||
saveReplaceOption(options, newDefaultApplyFilter, REFERENCES);
|
||||
saveReplaceOption(options, newDefaultApplyFilter, BOOKMARKS);
|
||||
saveReplaceOption(options, newDefaultApplyFilter, PROPERTIES);
|
||||
saveReplaceOption(options, newDefaultApplyFilter, FUNCTIONS);
|
||||
|
||||
saveMergeOption(options, newDefaultApplyFilter, PLATE_COMMENTS);
|
||||
saveMergeOption(options, newDefaultApplyFilter, PRE_COMMENTS);
|
||||
saveMergeOption(options, newDefaultApplyFilter, EOL_COMMENTS);
|
||||
saveMergeOption(options, newDefaultApplyFilter, REPEATABLE_COMMENTS);
|
||||
saveMergeOption(options, newDefaultApplyFilter, POST_COMMENTS);
|
||||
|
||||
saveMergeOption(options, newDefaultApplyFilter, FUNCTION_TAGS);
|
||||
|
||||
saveCodeUnitReplaceOption(options, newDefaultApplyFilter, CODE_UNITS);
|
||||
saveSymbolMergeOption(options, newDefaultApplyFilter, SYMBOLS);
|
||||
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
private void saveCodeUnitReplaceOption(Options options, ProgramMergeFilter defaultApplyFilter,
|
||||
int setting) {
|
||||
int filter =
|
||||
(defaultApplyFilter.getFilter(ProgramMergeFilter.INSTRUCTIONS) >= defaultApplyFilter.getFilter(ProgramMergeFilter.DATA)) ? ProgramMergeFilter.INSTRUCTIONS
|
||||
: ProgramMergeFilter.DATA;
|
||||
REPLACE_CHOICE defaultSetting = REPLACE_CHOICE.REPLACE;
|
||||
REPLACE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
|
||||
REPLACE_CHOICE diffSetting = convertTypeToReplaceEnum(defaultApplyFilter, filter);
|
||||
if (!diffSetting.equals(optionSetting)) {
|
||||
options.setEnum(getOptionName(setting), diffSetting);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveReplaceOption(Options options, ProgramMergeFilter defaultApplyFilter,
|
||||
int setting) {
|
||||
REPLACE_CHOICE defaultSetting = REPLACE_CHOICE.REPLACE;
|
||||
REPLACE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
|
||||
REPLACE_CHOICE diffSetting =
|
||||
convertTypeToReplaceEnum(defaultApplyFilter, getMergeFilterType(setting));
|
||||
if (!diffSetting.equals(optionSetting)) {
|
||||
options.setEnum(getOptionName(setting), diffSetting);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMergeOption(Options options, ProgramMergeFilter defaultApplyFilter, int setting) {
|
||||
MERGE_CHOICE defaultSetting = MERGE_CHOICE.MERGE;
|
||||
MERGE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
|
||||
MERGE_CHOICE diffSetting =
|
||||
convertTypeToMergeEnum(defaultApplyFilter, getMergeFilterType(setting));
|
||||
if (!diffSetting.equals(optionSetting)) {
|
||||
options.setEnum(getOptionName(setting), diffSetting);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSymbolMergeOption(Options options, ProgramMergeFilter defaultApplyFilter,
|
||||
int setting) {
|
||||
SYMBOL_MERGE_CHOICE defaultSetting = SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY;
|
||||
SYMBOL_MERGE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
|
||||
SYMBOL_MERGE_CHOICE diffSetting = getSymbolMergeEnum(defaultApplyFilter);
|
||||
if (!diffSetting.equals(optionSetting)) {
|
||||
options.setEnum(getOptionName(setting), diffSetting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param applySetting
|
||||
* @return
|
||||
*/
|
||||
private int getMergeFilterType(int applySetting) {
|
||||
switch (applySetting) {
|
||||
case PROGRAM_CONTEXT:
|
||||
return ProgramMergeFilter.PROGRAM_CONTEXT;
|
||||
case BYTES:
|
||||
return ProgramMergeFilter.BYTES;
|
||||
case CODE_UNITS:
|
||||
return ProgramMergeFilter.CODE_UNITS;
|
||||
case REFERENCES:
|
||||
return ProgramMergeFilter.REFERENCES;
|
||||
case BOOKMARKS:
|
||||
return ProgramMergeFilter.BOOKMARKS;
|
||||
case PROPERTIES:
|
||||
return ProgramMergeFilter.PROPERTIES;
|
||||
case FUNCTIONS:
|
||||
return ProgramMergeFilter.FUNCTIONS;
|
||||
case PLATE_COMMENTS:
|
||||
return ProgramMergeFilter.PLATE_COMMENTS;
|
||||
case PRE_COMMENTS:
|
||||
return ProgramMergeFilter.PRE_COMMENTS;
|
||||
case EOL_COMMENTS:
|
||||
return ProgramMergeFilter.EOL_COMMENTS;
|
||||
case REPEATABLE_COMMENTS:
|
||||
return ProgramMergeFilter.REPEATABLE_COMMENTS;
|
||||
case POST_COMMENTS:
|
||||
return ProgramMergeFilter.POST_COMMENTS;
|
||||
case SYMBOLS:
|
||||
return ProgramMergeFilter.SYMBOLS;
|
||||
case FUNCTION_TAGS:
|
||||
return ProgramMergeFilter.FUNCTION_TAGS;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param applySetting
|
||||
* @return
|
||||
*/
|
||||
private String getOptionName(int applySetting) {
|
||||
switch (applySetting) {
|
||||
case PROGRAM_CONTEXT:
|
||||
return OPTION_PROGRAM_CONTEXT;
|
||||
case BYTES:
|
||||
return OPTION_BYTES;
|
||||
case CODE_UNITS:
|
||||
return OPTION_CODE_UNITS;
|
||||
case REFERENCES:
|
||||
return OPTION_REFERENCES;
|
||||
case BOOKMARKS:
|
||||
return OPTION_BOOKMARKS;
|
||||
case PROPERTIES:
|
||||
return OPTION_PROPERTIES;
|
||||
case FUNCTIONS:
|
||||
return OPTION_FUNCTIONS;
|
||||
case PLATE_COMMENTS:
|
||||
return OPTION_PLATE_COMMENTS;
|
||||
case PRE_COMMENTS:
|
||||
return OPTION_PRE_COMMENTS;
|
||||
case EOL_COMMENTS:
|
||||
return OPTION_EOL_COMMENTS;
|
||||
case REPEATABLE_COMMENTS:
|
||||
return OPTION_REPEATABLE_COMMENTS;
|
||||
case POST_COMMENTS:
|
||||
return OPTION_POST_COMMENTS;
|
||||
case SYMBOLS:
|
||||
return OPTION_SYMBOLS;
|
||||
case FUNCTION_TAGS:
|
||||
return OPTION_FUNCTION_TAGS;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Converts the current value of the indicated StringEnum to a ProgramMergeFilter type's individual filter setting.
|
||||
// * @param option The StringEnum with a value to be checked.
|
||||
// * @return the individual filter for the apply setting associated with the StringEnum.
|
||||
// */
|
||||
// private int convertOptionToFilter(StringEnum option) {
|
||||
// return convertIndexToFilter(option.getSelectedValueIndex());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Converts the "combined" option for symbols into just the MERGE_CHOICE which is used
|
||||
* to decide if the symbol should be ignored, replaced, or merged.
|
||||
* @param the SYMBOL_MERGE_CHOICE
|
||||
* @return the MERGE_CHOICE
|
||||
*/
|
||||
MERGE_CHOICE convertSymbolMergeChoiceToMergeChoice(SYMBOL_MERGE_CHOICE symbolMergeChoice) {
|
||||
switch (symbolMergeChoice) {
|
||||
default:
|
||||
case IGNORE:
|
||||
return MERGE_CHOICE.IGNORE;
|
||||
case REPLACE:
|
||||
return MERGE_CHOICE.REPLACE;
|
||||
case MERGE_DONT_SET_PRIMARY:
|
||||
case MERGE_AND_SET_PRIMARY:
|
||||
return MERGE_CHOICE.MERGE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the "combined" option for symbols into just the REPLACE_CHOICE which is used
|
||||
* to decide if the "primary" attribute should be used.
|
||||
* @param the SYMBOL_MERGE_CHOICE
|
||||
* @return the REPLACE_CHOICE
|
||||
*/
|
||||
REPLACE_CHOICE convertSymbolMergeChoiceToReplaceChoiceForPrimay(
|
||||
SYMBOL_MERGE_CHOICE symbolMergeChoice) {
|
||||
switch (symbolMergeChoice) {
|
||||
default:
|
||||
case IGNORE:
|
||||
case MERGE_DONT_SET_PRIMARY:
|
||||
return REPLACE_CHOICE.IGNORE;
|
||||
case REPLACE:
|
||||
case MERGE_AND_SET_PRIMARY:
|
||||
return REPLACE_CHOICE.REPLACE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the standard index number of a StringEnum for the "Symbols" option for the indicated
|
||||
* filter values of the symbols apply filter and primary symbol filter.
|
||||
* @param symbolFilter the ProgramMergeFilter.SYMBOLS filter.
|
||||
* @param primaryFilter the ProgramMergeFilter.PRIMARY_SYMBOL filter.
|
||||
* @return the index for setting the StringEnum to the value associated with the filter values.
|
||||
*/
|
||||
int convertFiltersToSymbolIndex(int symbolFilter, int primaryFilter) {
|
||||
switch (symbolFilter) {
|
||||
case ProgramMergeFilter.IGNORE:
|
||||
default:
|
||||
return 0; // Ignore
|
||||
case ProgramMergeFilter.REPLACE:
|
||||
return 1; // Replace
|
||||
case ProgramMergeFilter.MERGE:
|
||||
if (primaryFilter == ProgramMergeFilter.REPLACE) {
|
||||
return 3; // Merge 2 (Set Primary as in Program 2)
|
||||
}
|
||||
return 2; // Merge 1 (Set Primary as in Program 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a standard StringEnum for handling an option of the indicated type that allows Ignore and Replace.
|
||||
* The current selected item will be based on the filter's value.
|
||||
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
|
||||
* @param type the ProgramMergeFilter filter type
|
||||
* @return the StringEnum
|
||||
*/
|
||||
private REPLACE_CHOICE convertTypeToReplaceEnum(ProgramMergeFilter defaultApplyFilter, int type) {
|
||||
int filter = defaultApplyFilter.getFilter(type);
|
||||
return REPLACE_CHOICE.values()[filter];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a standard StringEnum for handling an option of the indicated type that allows Ignore, Replace and Merge.
|
||||
* The current selected item will be based on the filter's value.
|
||||
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
|
||||
* @param type the ProgramMergeFilter filter type
|
||||
* @return the StringEnum
|
||||
*/
|
||||
private MERGE_CHOICE convertTypeToMergeEnum(ProgramMergeFilter defaultApplyFilter, int type) {
|
||||
int filter = defaultApplyFilter.getFilter(type);
|
||||
return MERGE_CHOICE.values()[filter];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a standard StringEnum for the Symbols option.
|
||||
* The current selected item will be based on the filter's value.
|
||||
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
|
||||
* @param type the ProgramMergeFilter filter type
|
||||
* @return the StringEnum
|
||||
*/
|
||||
private SYMBOL_MERGE_CHOICE getSymbolMergeEnum(ProgramMergeFilter defaultApplyFilter) {
|
||||
int symbolsFilter = defaultApplyFilter.getFilter(ProgramMergeFilter.SYMBOLS);
|
||||
int primarySymbolFilter = defaultApplyFilter.getFilter(ProgramMergeFilter.PRIMARY_SYMBOL);
|
||||
int filter = convertFiltersToSymbolIndex(symbolsFilter, primarySymbolFilter);
|
||||
return SYMBOL_MERGE_CHOICE.values()[filter];
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Don't have any options change listeners to clean up.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,468 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.WindowPosition;
|
||||
import docking.widgets.VariableHeightPanel;
|
||||
import ghidra.app.plugin.core.diff.DiffApplySettingsOptionManager.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The DiffSettingsDialog is used to change the types of differences currently
|
||||
* highlighted. It also allows the user to change the types of differences being
|
||||
* applied and whether labels and/or comments are being merged or replaced.
|
||||
*/
|
||||
public class DiffApplySettingsProvider extends ComponentProviderAdapter {
|
||||
|
||||
public static final String APPLY_FILTER_CHANGED_ACTION = "Apply Filter Changed";
|
||||
public static final ImageIcon ICON = ResourceManager.loadImage("images/settings16.gif");
|
||||
public static final String TITLE = "Diff Apply Settings";
|
||||
|
||||
private ProgramDiffPlugin plugin;
|
||||
|
||||
private ArrayList<Choice> choices;
|
||||
private Choice programContextCB;
|
||||
private Choice bytesCB;
|
||||
private Choice codeUnitsCB;
|
||||
private Choice refsCB;
|
||||
private SymbolsChoice symbolsCB;
|
||||
private Choice plateCommentsCB;
|
||||
private Choice preCommentsCB;
|
||||
private Choice eolCommentsCB;
|
||||
private Choice repeatableCommentsCB;
|
||||
private Choice postCommentsCB;
|
||||
private Choice bookmarksCB;
|
||||
private Choice propertiesCB;
|
||||
private Choice functionsCB;
|
||||
private Choice functionTagsCB;
|
||||
|
||||
private int applyProgramContext;
|
||||
private int applyBytes;
|
||||
private int applyCodeUnits;
|
||||
private int applyReferences;
|
||||
private int applyPlateComments;
|
||||
private int applyPreComments;
|
||||
private int applyEolComments;
|
||||
private int applyRepeatableComments;
|
||||
private int applyPostComments;
|
||||
private int applySymbols;
|
||||
private int applyBookmarks;
|
||||
private int applyProperties;
|
||||
private int applyFunctions;
|
||||
private int applyFunctionTags;
|
||||
private int replacePrimary;
|
||||
|
||||
private ProgramMergeFilter applyFilter;
|
||||
private boolean adjustingApplyFilter = false;
|
||||
private JComponent applyPanel;
|
||||
private ArrayList<ActionListener> listenerList = new ArrayList<>();
|
||||
private boolean pgmContextEnabled = true;
|
||||
|
||||
public DiffApplySettingsProvider(ProgramDiffPlugin plugin) {
|
||||
super(plugin.getTool(), TITLE, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
setIcon(ICON);
|
||||
setWindowMenuGroup("Diff");
|
||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||
|
||||
// transient so that is only there while the Diff is available
|
||||
setTransient();
|
||||
|
||||
setHelpLocation(new HelpLocation("Diff", "Diff_Apply_Settings"));
|
||||
applyPanel = createApplyFilterPanel();
|
||||
applyPanel.setName("Diff Apply Settings Panel");
|
||||
}
|
||||
|
||||
public void configure(ProgramMergeFilter apply) {
|
||||
setApplyFilter(apply);
|
||||
}
|
||||
|
||||
public void addActions() {
|
||||
plugin.getTool().addLocalAction(this,
|
||||
new SaveApplySettingsAction(this, plugin.applySettingsMgr));
|
||||
plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
|
||||
plugin.getTool().addLocalAction(this, new DiffReplaceAllAction(this));
|
||||
plugin.getTool().addLocalAction(this, new DiffMergeAllAction(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the user choices to indicate the filter settings.
|
||||
*/
|
||||
private void createChoices() {
|
||||
choices = new ArrayList<>();
|
||||
programContextCB = new Choice("Program Context", false);
|
||||
programContextCB.addActionListener(e -> {
|
||||
applyProgramContext = programContextCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PROGRAM_CONTEXT, applyProgramContext);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(programContextCB);
|
||||
|
||||
bytesCB = new Choice("Bytes", false);
|
||||
bytesCB.addActionListener(e -> {
|
||||
applyBytes = bytesCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.BYTES, applyBytes);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(bytesCB);
|
||||
|
||||
codeUnitsCB = new Choice("Code Units", false);
|
||||
codeUnitsCB.addActionListener(e -> {
|
||||
applyCodeUnits = codeUnitsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS | ProgramMergeFilter.EQUATES,
|
||||
applyCodeUnits);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(codeUnitsCB);
|
||||
|
||||
refsCB = new Choice("References", false);
|
||||
refsCB.addActionListener(e -> {
|
||||
applyReferences = refsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.REFERENCES, applyReferences);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(refsCB);
|
||||
|
||||
plateCommentsCB = new Choice("Plate Comments", true);
|
||||
plateCommentsCB.addActionListener(e -> {
|
||||
applyPlateComments = plateCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PLATE_COMMENTS, applyPlateComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(plateCommentsCB);
|
||||
|
||||
preCommentsCB = new Choice("Pre Comments", true);
|
||||
preCommentsCB.addActionListener(e -> {
|
||||
applyPreComments = preCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PRE_COMMENTS, applyPreComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(preCommentsCB);
|
||||
|
||||
eolCommentsCB = new Choice("Eol Comments", true);
|
||||
eolCommentsCB.addActionListener(e -> {
|
||||
applyEolComments = eolCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.EOL_COMMENTS, applyEolComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(eolCommentsCB);
|
||||
|
||||
repeatableCommentsCB = new Choice("Repeatable Comments", true);
|
||||
repeatableCommentsCB.addActionListener(e -> {
|
||||
applyRepeatableComments = repeatableCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.REPEATABLE_COMMENTS, applyRepeatableComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(repeatableCommentsCB);
|
||||
|
||||
postCommentsCB = new Choice("Post Comments", true);
|
||||
postCommentsCB.addActionListener(e -> {
|
||||
applyPostComments = postCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.POST_COMMENTS, applyPostComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(postCommentsCB);
|
||||
|
||||
symbolsCB = new SymbolsChoice();
|
||||
symbolsCB.addActionListener(e -> {
|
||||
SYMBOL_MERGE_CHOICE symbols =
|
||||
SYMBOL_MERGE_CHOICE.values()[symbolsCB.getSelectedIndex()];
|
||||
MERGE_CHOICE merge =
|
||||
plugin.applySettingsMgr.convertSymbolMergeChoiceToMergeChoice(symbols);
|
||||
REPLACE_CHOICE primary =
|
||||
plugin.applySettingsMgr.convertSymbolMergeChoiceToReplaceChoiceForPrimay(symbols);
|
||||
applySymbols = merge.ordinal();
|
||||
replacePrimary = primary.ordinal();
|
||||
applyFilter.setFilter(ProgramMergeFilter.SYMBOLS, applySymbols);
|
||||
applyFilter.setFilter(ProgramMergeFilter.PRIMARY_SYMBOL, replacePrimary);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(symbolsCB);
|
||||
|
||||
bookmarksCB = new Choice("Bookmarks", false);
|
||||
bookmarksCB.addActionListener(e -> {
|
||||
applyBookmarks = bookmarksCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.BOOKMARKS, applyBookmarks);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(bookmarksCB);
|
||||
|
||||
propertiesCB = new Choice("Properties", false);
|
||||
propertiesCB.addActionListener(e -> {
|
||||
applyProperties = propertiesCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PROPERTIES, applyProperties);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(propertiesCB);
|
||||
|
||||
functionsCB = new Choice("Functions", false);
|
||||
functionsCB.addActionListener(e -> {
|
||||
applyFunctions = functionsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.FUNCTIONS, applyFunctions);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(functionsCB);
|
||||
|
||||
functionTagsCB = new Choice("Function Tags", true);
|
||||
functionTagsCB.addActionListener(e -> {
|
||||
applyFunctionTags = functionTagsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.FUNCTION_TAGS, applyFunctionTags);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(functionTagsCB);
|
||||
|
||||
int maxLabelWidth = 0;
|
||||
int maxComboWidth = 0;
|
||||
for (Choice choice : choices) {
|
||||
maxLabelWidth = Math.max(maxLabelWidth, choice.label.getPreferredSize().width);
|
||||
maxComboWidth = Math.max(maxComboWidth, choice.applyCB.getPreferredSize().width);
|
||||
}
|
||||
for (Choice choice : choices) {
|
||||
int height = choice.label.getPreferredSize().height;
|
||||
choice.label.setPreferredSize(new Dimension(maxLabelWidth, height));
|
||||
height = choice.applyCB.getPreferredSize().height;
|
||||
choice.applyCB.setPreferredSize(new Dimension(maxComboWidth, height));
|
||||
}
|
||||
Collections.sort(choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the checkboxes to indicate the filter settings.
|
||||
*/
|
||||
private JComponent createApplyFilterPanel() {
|
||||
createChoices();
|
||||
VariableHeightPanel panel = new VariableHeightPanel(false, 10, 3);
|
||||
|
||||
panel.setToolTipText(
|
||||
"<HTML>" + "For each difference type, select whether to ignore, replace, or merge." +
|
||||
"<BR>  <B>Ignore</B> - don't apply this type of difference." +
|
||||
"<BR>  <B>Replace</B> - replace the difference type with the one from program 2." +
|
||||
"<BR>  <B>Merge</B> - merge the difference type from program 2 with what's there." +
|
||||
"</HTML>");
|
||||
for (Choice choice : choices) {
|
||||
panel.add(choice);
|
||||
}
|
||||
|
||||
return new JScrollPane(panel);
|
||||
}
|
||||
|
||||
private void adjustApplyFilter() {
|
||||
try {
|
||||
adjustingApplyFilter = true;
|
||||
programContextCB.setSelectedIndex(applyProgramContext);
|
||||
bytesCB.setSelectedIndex(applyBytes);
|
||||
codeUnitsCB.setSelectedIndex(applyCodeUnits);
|
||||
refsCB.setSelectedIndex(applyReferences);
|
||||
plateCommentsCB.setSelectedIndex(applyPlateComments);
|
||||
preCommentsCB.setSelectedIndex(applyPreComments);
|
||||
eolCommentsCB.setSelectedIndex(applyEolComments);
|
||||
repeatableCommentsCB.setSelectedIndex(applyRepeatableComments);
|
||||
postCommentsCB.setSelectedIndex(applyPostComments);
|
||||
int symbolsIndex =
|
||||
plugin.applySettingsMgr.convertFiltersToSymbolIndex(applySymbols, replacePrimary);
|
||||
symbolsCB.setSelectedIndex(symbolsIndex);
|
||||
bookmarksCB.setSelectedIndex(applyBookmarks);
|
||||
propertiesCB.setSelectedIndex(applyProperties);
|
||||
functionsCB.setSelectedIndex(applyFunctions);
|
||||
functionTagsCB.setSelectedIndex(applyFunctionTags);
|
||||
}
|
||||
finally {
|
||||
adjustingApplyFilter = false;
|
||||
applyFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void setPgmContextEnabled(boolean enable) {
|
||||
pgmContextEnabled = enable;
|
||||
if (!pgmContextEnabled) {
|
||||
applyProgramContext = 0;
|
||||
programContextCB.setSelectedIndex(applyProgramContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the merge filter for applying differences.
|
||||
* @return the current merge Filter settings.
|
||||
*/
|
||||
ProgramMergeFilter getApplyFilter() {
|
||||
return new ProgramMergeFilter(applyFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the diff tool merge filter settings for applying differences.
|
||||
* @param filter the new apply Filter settings.
|
||||
*/
|
||||
void setApplyFilter(ProgramMergeFilter filter) {
|
||||
applyFilter = new ProgramMergeFilter(filter);
|
||||
applyProgramContext = applyFilter.getFilter(ProgramMergeFilter.PROGRAM_CONTEXT);
|
||||
applyBytes = applyFilter.getFilter(ProgramMergeFilter.BYTES);
|
||||
applyCodeUnits = Math.max(applyFilter.getFilter(ProgramMergeFilter.INSTRUCTIONS),
|
||||
applyFilter.getFilter(ProgramMergeFilter.DATA));
|
||||
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS, applyCodeUnits);
|
||||
applyReferences = applyFilter.getFilter(ProgramMergeFilter.REFERENCES);
|
||||
applyPlateComments = applyFilter.getFilter(ProgramMergeFilter.PLATE_COMMENTS);
|
||||
applyPreComments = applyFilter.getFilter(ProgramMergeFilter.PRE_COMMENTS);
|
||||
applyEolComments = applyFilter.getFilter(ProgramMergeFilter.EOL_COMMENTS);
|
||||
applyRepeatableComments = applyFilter.getFilter(ProgramMergeFilter.REPEATABLE_COMMENTS);
|
||||
applyPostComments = applyFilter.getFilter(ProgramMergeFilter.POST_COMMENTS);
|
||||
applySymbols = applyFilter.getFilter(ProgramMergeFilter.SYMBOLS);
|
||||
applyBookmarks = applyFilter.getFilter(ProgramMergeFilter.BOOKMARKS);
|
||||
applyProperties = applyFilter.getFilter(ProgramMergeFilter.PROPERTIES);
|
||||
applyFunctions = applyFilter.getFilter(ProgramMergeFilter.FUNCTIONS);
|
||||
applyFunctionTags = applyFilter.getFilter(ProgramMergeFilter.FUNCTION_TAGS);
|
||||
replacePrimary = applyFilter.getFilter(ProgramMergeFilter.PRIMARY_SYMBOL);
|
||||
|
||||
adjustApplyFilter();
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if at least one of the checkboxes for the filter
|
||||
* has been selected.
|
||||
*/
|
||||
boolean hasApplySelection() {
|
||||
return ((applyProgramContext | applyBytes | applyCodeUnits | applyReferences |
|
||||
applyPlateComments | applyPreComments | applyEolComments | applyRepeatableComments |
|
||||
applyPostComments | applySymbols | applyBookmarks | applyProperties | applyFunctions |
|
||||
applyFunctionTags) != 0);
|
||||
}
|
||||
|
||||
protected void applyFilterChanged() {
|
||||
if (adjustingApplyFilter) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
ActionListener listener = listenerList.get(i);
|
||||
listener.actionPerformed(new ActionEvent(this, 0, APPLY_FILTER_CHANGED_ACTION));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
// overridden to not remove this transient provider
|
||||
plugin.getTool().showComponentProvider(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return applyPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin associated with this provider.
|
||||
*/
|
||||
Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
class Choice extends JPanel implements Comparable<Choice> {
|
||||
private final static long serialVersionUID = 1L;
|
||||
String type;
|
||||
boolean allowMerge;
|
||||
JLabel label;
|
||||
JComboBox<Enum<?>> applyCB;
|
||||
|
||||
public Choice(String type, boolean allowMerge) {
|
||||
setLayout(new BorderLayout());
|
||||
this.type = type;
|
||||
this.allowMerge = allowMerge;
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
applyCB =
|
||||
new JComboBox<>(allowMerge ? DiffApplySettingsOptionManager.MERGE_CHOICE.values()
|
||||
: DiffApplySettingsOptionManager.REPLACE_CHOICE.values());
|
||||
applyCB.setName(type + " Diff Apply CB");
|
||||
String typeName = type;
|
||||
if (typeName.endsWith(" Comments")) {
|
||||
typeName = "Comments, " + typeName.substring(0, typeName.length() - 9);
|
||||
}
|
||||
label = new JLabel(" " + typeName + " ");
|
||||
label.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
add(applyCB, BorderLayout.EAST);
|
||||
add(label, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void setSelectedIndex(int index) {
|
||||
applyCB.setSelectedIndex(index);
|
||||
}
|
||||
|
||||
int getSelectedIndex() {
|
||||
return applyCB.getSelectedIndex();
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
applyCB.addActionListener(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
applyCB.removeActionListener(listener);
|
||||
}
|
||||
|
||||
void setLabelSize(Dimension preferredSize) {
|
||||
label.setPreferredSize(preferredSize);
|
||||
}
|
||||
|
||||
void setComboSize(Dimension preferredSize) {
|
||||
applyCB.setPreferredSize(preferredSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Choice o) {
|
||||
return label.toString().compareTo(o.label.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SymbolsChoice extends Choice {
|
||||
private final static long serialVersionUID = 1L;
|
||||
|
||||
public SymbolsChoice() {
|
||||
super("Labels", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
applyCB = new JComboBox<>(DiffApplySettingsOptionManager.SYMBOL_MERGE_CHOICE.values());
|
||||
applyCB.setName(type + " Diff Apply CB");
|
||||
label = new JLabel(" " + type + " ");
|
||||
label.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
add(applyCB, BorderLayout.EAST);
|
||||
add(label, BorderLayout.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,402 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* DiffController controls a program Diff. It maintains address sets indicating
|
||||
* the differences between two programs. It can limit the determined differences
|
||||
* to an address set. It allows differences to be applied or ignored. It has a
|
||||
* diff filter that controls the differences being indicated. It has a merge
|
||||
* filter that controls the types of differences being applied. It allows
|
||||
* differences at particular addresses to be ignored.
|
||||
*
|
||||
* The Diff controller also maintains the current location. It provides a way
|
||||
* to navigate from one difference to the next or previous difference.
|
||||
*/
|
||||
public class DiffController {
|
||||
private ProgramMergeManager mergeEngine;
|
||||
private AddressSetView p1LastDiffs;
|
||||
private AddressSetView p1LimitSet;
|
||||
private Address p1CurrentAddress;
|
||||
private ArrayList<DiffControllerListener> listenerList =
|
||||
new ArrayList<DiffControllerListener>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* <P>Note: This method is potentially time consuming and should normally
|
||||
* be called from within a background task.
|
||||
* @param p1 The first program to apply differences to.
|
||||
* @param p2 The second program to apply differences from.
|
||||
* @param p1LimitSet Address set the determined differences are limited to.
|
||||
* The addresses in this set should be derived from p1.
|
||||
* @param diffFilter filter indicating difference types to mark.
|
||||
* @param mergeFilter filter indicating difference types to apply.
|
||||
* @param monitor the progress monitor
|
||||
* @throws ProgramConflictException
|
||||
*/
|
||||
public DiffController(Program p1, Program p2, AddressSetView p1LimitSet,
|
||||
ProgramDiffFilter diffFilter, ProgramMergeFilter mergeFilter, TaskMonitor monitor)
|
||||
throws ProgramConflictException {
|
||||
mergeEngine = new ProgramMergeManager(p1, p2, p1LimitSet, monitor);
|
||||
mergeEngine.setDiffFilter(diffFilter);
|
||||
mergeEngine.setMergeFilter(mergeFilter);
|
||||
this.p1LimitSet = p1LimitSet;
|
||||
if (p1LimitSet == null) {
|
||||
p1CurrentAddress = p1.getMinAddress();
|
||||
}
|
||||
else {
|
||||
p1CurrentAddress = p1LimitSet.getMinAddress();
|
||||
}
|
||||
p1LastDiffs = new AddressSet();
|
||||
}
|
||||
|
||||
/** Gets the first program being compared by the ProgramDiff.
|
||||
* @return program1. the program to apply differences to.
|
||||
*/
|
||||
Program getProgramOne() {
|
||||
return mergeEngine.getProgramOne();
|
||||
}
|
||||
|
||||
/** Gets the second program being compared by the ProgramDiff.
|
||||
* @return program2. the program to get differences from for applying.
|
||||
*/
|
||||
Program getProgramTwo() {
|
||||
return mergeEngine.getProgramTwo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set the current Program Diff is limited to.
|
||||
* @return the address set the current diff is limited to.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getLimitedAddressSet() {
|
||||
return p1LimitSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set indicating the addresses currently being ignored.
|
||||
* @return the address set indicating the addresses currently being ignored.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getIgnoredAddressSet() {
|
||||
return mergeEngine.getIgnoreAddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set being used to restrict the resulting difference set
|
||||
* that is reported by getting the differences. This address set is
|
||||
* effectively a view port into the differences address set.
|
||||
* @return the address set used to restrict the differences.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getRestrictedAddressSet() {
|
||||
return mergeEngine.getRestrictedAddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the diff filter that the merge is using.
|
||||
*/
|
||||
public ProgramDiffFilter getDiffFilter() {
|
||||
return mergeEngine.getDiffFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter that indicates which parts of the Program should be
|
||||
* diffed.
|
||||
*/
|
||||
public void setDiffFilter(ProgramDiffFilter filter) {
|
||||
mergeEngine.setDiffFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the filter that indicates which parts of the Program
|
||||
* should be merged.
|
||||
*/
|
||||
public ProgramMergeFilter getMergeFilter() {
|
||||
return mergeEngine.getMergeFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter that indicates which parts of the Program should be
|
||||
* merged.
|
||||
*/
|
||||
public void setMergeFilter(ProgramMergeFilter filter) {
|
||||
mergeEngine.setMergeFilter(filter);
|
||||
}
|
||||
|
||||
/** Gets the filtered program differences for this merge. Only differences are
|
||||
* indicated for merge filter categories that are enabled and for addresses
|
||||
* that have not been marked as ignored.
|
||||
* <P>Note: This method is potentially time consuming and should normally
|
||||
* be called from within a background task.
|
||||
* @param monitor the task monitor for indicating the progress of
|
||||
* determining differences. This monitor also allows the user to cancel if
|
||||
* the diff takes too long. If no monitor is desired, use null.
|
||||
* @return the program differences.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
public AddressSetView getFilteredDifferences(TaskMonitor monitor) throws CancelledException {
|
||||
AddressSetView diffs1 = mergeEngine.getFilteredDifferences(monitor);
|
||||
Program program1 = getProgramOne();
|
||||
Program program2 = getProgramTwo();
|
||||
monitor.setMessage("Adjusting differences to code unit boundaries...");
|
||||
AddressSet diffSet2 = DiffUtility.getCompatibleAddressSet(diffs1, program2);
|
||||
AddressSet diffCuSet2 = DiffUtility.getCodeUnitSet(diffSet2, program2);
|
||||
monitor.setMessage("Converting Diffs to program 1 set...");
|
||||
diffs1 = DiffUtility.getCompatibleAddressSet(diffCuSet2, program1);
|
||||
if (!p1LastDiffs.equals(diffs1)) {
|
||||
p1LastDiffs = diffs1;
|
||||
}
|
||||
return diffs1;
|
||||
}
|
||||
|
||||
/** Restrict the resulting differences to the indicated address set.
|
||||
* @param p1AddressSet the address set to restrict the getFilteredDifferences() to.
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the restriction.
|
||||
*/
|
||||
public void restrictResults(AddressSetView p1AddressSet, TaskMonitor monitor) {
|
||||
mergeEngine.restrictResults(p1AddressSet);
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/** Remove the restriction for the resulting differences to the indicated address set.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the removal of the restriction.
|
||||
*/
|
||||
public void removeResultRestrictions(TaskMonitor monitor) {
|
||||
mergeEngine.removeResultRestrictions();
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply differences in the address set from program p2
|
||||
* into the current program p1.
|
||||
* @param p1AddressSet address set of differences
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
* @param filter merge filter
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the removal of the restriction.
|
||||
* @throws MemoryAccessException
|
||||
* @throws CancelledException if user cancels via the monitor.
|
||||
*/
|
||||
boolean apply(AddressSetView p1AddressSet, TaskMonitor monitor) throws MemoryAccessException,
|
||||
CancelledException {
|
||||
boolean applied = mergeEngine.merge(p1AddressSet, monitor);
|
||||
return applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any error and information messages associated with the last apply.
|
||||
*/
|
||||
String getApplyMessage() {
|
||||
return mergeEngine.getErrorMessage() + mergeEngine.getInfoMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore any differences in the specified address set.
|
||||
* @param p1AddressSet address set set of differences
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
*/
|
||||
void ignore(AddressSetView p1AddressSet, TaskMonitor monitor) {
|
||||
mergeEngine.ignore(p1AddressSet);
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any warning messages associated with the initial Diff of the two programs.
|
||||
*/
|
||||
public String getWarnings() {
|
||||
return mergeEngine.getWarnings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address for the diff controller's current location.
|
||||
* @return the current address.
|
||||
* This address is derived from the p1 program.
|
||||
*/
|
||||
Address getCurrentAddress() {
|
||||
return p1CurrentAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the given address; update the last address so that
|
||||
* iterator will be adjusted for next and previous.
|
||||
* @param p1Address address to go to
|
||||
* This address should be derived from the p1 program.
|
||||
*/
|
||||
private void goTo(Address p1Address) {
|
||||
p1CurrentAddress = p1Address;
|
||||
locationChanged(p1CurrentAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Diff controller's current address to the specified address.
|
||||
* @param p1NewAddress the address
|
||||
* This address should be derived from the p1 program.
|
||||
*/
|
||||
void setLocation(Address p1NewAddress) {
|
||||
if (p1NewAddress.equals(p1CurrentAddress))
|
||||
return;
|
||||
p1CurrentAddress = p1NewAddress;
|
||||
locationChanged(p1CurrentAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the cursor should move to the first difference.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void first() {
|
||||
if (p1LastDiffs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
goTo(p1LastDiffs.getMinAddress());
|
||||
}
|
||||
|
||||
boolean hasNext() {
|
||||
return getNextAddress() != null;
|
||||
}
|
||||
|
||||
private Address getNextAddress() {
|
||||
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, true);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = it.next();
|
||||
if (range.contains(p1CurrentAddress)) {
|
||||
if (it.hasNext()) {
|
||||
return it.next().getMinAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return range.getMinAddress();
|
||||
|
||||
}
|
||||
|
||||
private Address getPreviousAddress() {
|
||||
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, false);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = it.next();
|
||||
if (range.getMinAddress().equals(p1CurrentAddress)) {
|
||||
if (it.hasNext()) {
|
||||
return it.next().getMinAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return range.getMinAddress();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the "next diff" button is hit.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void next() {
|
||||
Address nextAddress = getNextAddress();
|
||||
if (nextAddress != null) {
|
||||
goTo(nextAddress);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasPrevious() {
|
||||
return getPreviousAddress() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the "previous diff" button is hit.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void previous() {
|
||||
Address previousAddress = getPreviousAddress();
|
||||
if (previousAddress != null) {
|
||||
goTo(previousAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the differences to show what is still different between the two
|
||||
* programs. After calling this method, any differences that were being
|
||||
* ignored are still being ignored. The differences are restricted to the
|
||||
* same address set as before the refresh.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* recompute of the differences.
|
||||
* @throws ProgramConflictException
|
||||
*/
|
||||
void refresh(boolean keepIgnored, TaskMonitor monitor) throws ProgramConflictException {
|
||||
AddressSetView ignoreSet = getIgnoredAddressSet();
|
||||
recomputeDiffs(monitor);
|
||||
if (keepIgnored) {
|
||||
mergeEngine.ignore(ignoreSet);
|
||||
}
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
private void recomputeDiffs(TaskMonitor monitor) throws ProgramConflictException {
|
||||
recomputeDiffs(getLimitedAddressSet(), monitor);
|
||||
}
|
||||
|
||||
private void recomputeDiffs(AddressSetView newLimitSet, TaskMonitor monitor)
|
||||
throws ProgramConflictException {
|
||||
Program p1 = mergeEngine.getProgramOne();
|
||||
Program p2 = mergeEngine.getProgramTwo();
|
||||
ProgramDiffFilter diffFilter = mergeEngine.getDiffFilter();
|
||||
ProgramMergeFilter mergeFilter = mergeEngine.getMergeFilter();
|
||||
this.p1LimitSet = newLimitSet;
|
||||
|
||||
mergeEngine = new ProgramMergeManager(p1, p2, newLimitSet, monitor);
|
||||
mergeEngine.setDiffFilter(diffFilter);
|
||||
mergeEngine.setMergeFilter(mergeFilter);
|
||||
}
|
||||
|
||||
public void addDiffControllerListener(DiffControllerListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeDiffControllerListener(DiffControllerListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
public void locationChanged(Address program1Location) {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
DiffControllerListener listener = listenerList.get(i);
|
||||
listener.diffLocationChanged(this, program1Location);
|
||||
}
|
||||
}
|
||||
|
||||
public void differencesChanged(TaskMonitor monitor) {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
DiffControllerListener listener = listenerList.get(i);
|
||||
listener.differencesChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface DiffControllerListener {
|
||||
|
||||
public abstract void diffLocationChanged(DiffController diffControl,
|
||||
Address location);
|
||||
|
||||
public abstract void differencesChanged(DiffController diffControl);
|
||||
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The DiffDetailsProvider is used to view the differences for an address or
|
||||
* code unit address range.
|
||||
*/
|
||||
public class DiffDetailsProvider extends ComponentProviderAdapter {
|
||||
public static final String DIFF_DETAILS_HIDDEN_ACTION = "Diff Details Hidden";
|
||||
public static final String AUTO_UPDATE_CHECK_BOX = "Auto Update Check Box";
|
||||
public static final String FILTER_DIFFS_CHECK_BOX = "Filter Diffs Check Box";
|
||||
public static final String DIFF_DETAILS_TEXT_AREA = "Diff Details Text Area";
|
||||
public static final String DIFF_DETAILS_PANEL = "Diff Location Details Panel";
|
||||
public static final ImageIcon ICON = ResourceManager.loadImage("images/xmag.png");
|
||||
public static final ImageIcon REFRESH_ICON = Icons.REFRESH_ICON;
|
||||
public static final String TITLE = "Diff Details";
|
||||
|
||||
private ProgramDiffPlugin plugin;
|
||||
private JTextPane textPane;
|
||||
private StyledDocument doc;
|
||||
private JCheckBox filterDiffsCB;
|
||||
private JCheckBox autoUpdateCB;
|
||||
private boolean filterDiffs = false;
|
||||
private boolean autoUpdate = false;
|
||||
private JComponent detailsPanel;
|
||||
private ArrayList<ActionListener> listenerList = new ArrayList<>();
|
||||
private ProgramLocation p1DetailsLocation;
|
||||
private AddressSetView detailsAddrSet;
|
||||
private boolean isDisplayed = false;
|
||||
|
||||
private SwingUpdateManager updateManager;
|
||||
private ProgramLocation currentLocation;
|
||||
|
||||
/**
|
||||
* @param plugin
|
||||
*/
|
||||
public DiffDetailsProvider(ProgramDiffPlugin plugin) {
|
||||
super(plugin.getTool(), "Diff Location Details", plugin.getName());
|
||||
setTitle(TITLE);
|
||||
this.plugin = plugin;
|
||||
setIcon(ICON);
|
||||
setTransient();
|
||||
setWindowMenuGroup("Diff");
|
||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||
setHelpLocation(new HelpLocation("Diff", "Diff_Location_Details"));
|
||||
detailsPanel = createDiffDetailsPanel();
|
||||
detailsPanel.setName(DIFF_DETAILS_PANEL);
|
||||
setAutoUpdate(true);
|
||||
setFilterDiffs(false);
|
||||
setUpRefreshDetailsUpdateManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selected
|
||||
*/
|
||||
public void setAutoUpdate(boolean selected) {
|
||||
autoUpdateCB.setSelected(selected);
|
||||
autoUpdate = selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selected
|
||||
*/
|
||||
public void setFilterDiffs(boolean selected) {
|
||||
filterDiffsCB.setSelected(selected);
|
||||
filterDiffs = selected;
|
||||
}
|
||||
|
||||
public void addActions() {
|
||||
DockingAction refreshDetailsAction =
|
||||
new DockingAction("Refresh Diff Details", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
};
|
||||
|
||||
refreshDetailsAction.setDescription(
|
||||
"Refresh the details to show any differences at the current location.");
|
||||
refreshDetailsAction.setEnabled(true);
|
||||
|
||||
refreshDetailsAction.setToolBarData(new ToolBarData(REFRESH_ICON, "Diff"));
|
||||
refreshDetailsAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, "Refresh Diff Details"));
|
||||
plugin.getTool().addLocalAction(this, refreshDetailsAction);
|
||||
// plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the automatically update checkbox for diff details.
|
||||
*/
|
||||
private void createAutoUpdateCheckBox() {
|
||||
|
||||
autoUpdateCB = new JCheckBox("Automatically Update Details", false);
|
||||
autoUpdateCB.setName(AUTO_UPDATE_CHECK_BOX);
|
||||
autoUpdateCB.addActionListener(e -> {
|
||||
autoUpdate = autoUpdateCB.isSelected();
|
||||
if (autoUpdate) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the only show the filtered differences checkbox for diff details
|
||||
*/
|
||||
private void createFilterDiffsCheckBox() {
|
||||
|
||||
filterDiffsCB = new JCheckBox("Only Show Expected Difference Types", false);
|
||||
filterDiffsCB.setName(FILTER_DIFFS_CHECK_BOX);
|
||||
filterDiffsCB.addActionListener(e -> {
|
||||
filterDiffs = filterDiffsCB.isSelected();
|
||||
if (autoUpdateCB.isSelected()) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param p1Location
|
||||
*/
|
||||
protected void locationChanged(ProgramLocation p1Location) {
|
||||
if (isDisplayed && autoUpdate) {
|
||||
refreshDetails(p1Location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the displayed Diff Details for the indicated program location address.
|
||||
* @param p1Location This should be a program1 location.
|
||||
*/
|
||||
void refreshDetails(ProgramLocation p1Location) {
|
||||
if (p1Location == null) {
|
||||
return;
|
||||
}
|
||||
currentLocation = p1Location;
|
||||
updateManager.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a swing update manager that is used to refresh the DiffDetails.
|
||||
*/
|
||||
private void setUpRefreshDetailsUpdateManager() {
|
||||
updateManager = new SwingUpdateManager(100, 4000, () -> doRefreshDetails(currentLocation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the displayed Diff Details for the indicated program location address.
|
||||
* @param p1Location This should be a program1 location.
|
||||
*/
|
||||
private void doRefreshDetails(ProgramLocation p1Location) {
|
||||
if (p1Location == null) {
|
||||
return;
|
||||
}
|
||||
// Do nothing if not visible.
|
||||
if (!isDisplayed) {
|
||||
return;
|
||||
}
|
||||
|
||||
Program p1 = p1Location.getProgram();
|
||||
if (p1 != plugin.getFirstProgram() && p1 != plugin.getSecondProgram()) {
|
||||
return; // If this location isn't for our Diff then ignore it. May be switching program tabs.
|
||||
}
|
||||
if (p1DetailsLocation != p1Location) {
|
||||
p1DetailsLocation = p1Location;
|
||||
}
|
||||
AddressSetView newAddrSet = getDetailsAddressSet(p1DetailsLocation);
|
||||
|
||||
if (newAddrSet == null) {
|
||||
setDocumentToErrorMessage("Must have a second program open to determine Diff details.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newAddrSet.equals(detailsAddrSet)) {
|
||||
detailsAddrSet = newAddrSet;
|
||||
}
|
||||
Address p1Address = p1DetailsLocation.getAddress();
|
||||
try {
|
||||
if (filterDiffs) {
|
||||
getFilteredDiffDetails(p1Address);
|
||||
}
|
||||
else {
|
||||
getDiffDetails(p1Address);
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
setDocumentToErrorMessage(
|
||||
"Failed to determine Diff Details due to concurrent program changes.\n" +
|
||||
"This may be caused by background analysis activity.\n" +
|
||||
" *** Press the Refresh button to update *** ");
|
||||
|
||||
// The program is being modified while this is trying to get the details.
|
||||
// If we want to automatically try again, this would need to re-issue the update
|
||||
// by calling updateManager.updateLater() here.
|
||||
}
|
||||
}
|
||||
|
||||
private void setDocumentToErrorMessage(String message) {
|
||||
try {
|
||||
doc.remove(0, doc.getLength());
|
||||
doc.insertString(0, message, new SimpleAttributeSet());
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Diff Details at the indicated address
|
||||
* @param p1Address the program1 address
|
||||
* @throws ConcurrentModificationException if analysis is modifying the program.
|
||||
*/
|
||||
private void getDiffDetails(Address p1Address) {
|
||||
plugin.addDiffDetails(p1Address, doc);
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
|
||||
private void getFilteredDiffDetails(Address p1Address) {
|
||||
plugin.addFilteredDiffDetails(p1Address, plugin.getDiffController().getDiffFilter(), doc);
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set where detailed differences will be determined for details at the
|
||||
* indicated address. An address set is returned since the indicated address may be in different
|
||||
* sized code units in each of the two programs.
|
||||
* @param p1Location the program location in program1 where details are desired.
|
||||
* @return the address set for code units containing that address within the programs being
|
||||
* compared to determine differences.
|
||||
* Otherwise null if a diff of two programs isn't being performed.
|
||||
*/
|
||||
private AddressSetView getDetailsAddressSet(ProgramLocation p1Location) {
|
||||
Address p1Address = p1Location.getAddress();
|
||||
return plugin.getDetailsAddressSet(p1Address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the Diff details and auto update checkbox.
|
||||
*/
|
||||
private JPanel createDiffDetailsPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setPreferredSize(new Dimension(600, 400));
|
||||
JScrollPane scrolledDetails = createDetailsPane();
|
||||
panel.add(scrolledDetails, BorderLayout.CENTER);
|
||||
createAutoUpdateCheckBox();
|
||||
createFilterDiffsCheckBox();
|
||||
JPanel bottomPanel = new JPanel();
|
||||
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
|
||||
bottomPanel.add(Box.createHorizontalGlue());
|
||||
bottomPanel.add(autoUpdateCB);
|
||||
bottomPanel.add(Box.createHorizontalStrut(10));
|
||||
bottomPanel.add(filterDiffsCB);
|
||||
bottomPanel.add(Box.createHorizontalGlue());
|
||||
panel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JScrollPane createDetailsPane() {
|
||||
Font font = new Font("Monospaced", Font.PLAIN, 12);
|
||||
textPane = new JTextPane();
|
||||
doc = textPane.getStyledDocument();
|
||||
textPane.setName(DIFF_DETAILS_TEXT_AREA);
|
||||
textPane.setEditable(false);
|
||||
textPane.setMargin(new Insets(5, 5, 5, 5));
|
||||
textPane.setFont(font);
|
||||
textPane.setOpaque(true);
|
||||
textPane.setCaretPosition(0);
|
||||
JScrollPane scrolledDetails = new JScrollPane(textPane);
|
||||
JViewport vp = scrolledDetails.getViewport();
|
||||
vp.add(textPane);
|
||||
return scrolledDetails;
|
||||
}
|
||||
|
||||
StyledDocument getStyledDocument() {
|
||||
return doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
ActionListener listener = listenerList.get(i);
|
||||
listener.actionPerformed(new ActionEvent(this, 0, DIFF_DETAILS_HIDDEN_ACTION));
|
||||
}
|
||||
isDisplayed = false;
|
||||
p1DetailsLocation = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown() {
|
||||
isDisplayed = true;
|
||||
refreshDetails(plugin.getProgramLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
// overridden to not remove this transient provider
|
||||
plugin.getTool().showComponentProvider(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return detailsPanel;
|
||||
}
|
||||
|
||||
Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToHelper;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A service that provides the ability to go to a particular address or location in the
|
||||
* right-hand listing of the Diff.
|
||||
*/
|
||||
public class DiffGoToService implements GoToService {
|
||||
|
||||
private GoToService goToService;
|
||||
private ProgramDiffPlugin diffPlugin;
|
||||
private GoToHelper helper;
|
||||
|
||||
/**
|
||||
* Creates a service that provides the ability to go to a particular address or location
|
||||
* in the right-hand listing of the Diff.
|
||||
* @param goToService basic GoToService for the left-side listing that this will override
|
||||
* so it can go to addresses and locations in the right-side listing.
|
||||
* @param diffPlugin the plugin which provides the Diff capability.
|
||||
*/
|
||||
public DiffGoToService(GoToService goToService, ProgramDiffPlugin diffPlugin) {
|
||||
this.goToService = goToService;
|
||||
this.diffPlugin = diffPlugin;
|
||||
helper = new GoToHelper(diffPlugin.getTool());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoToOverrideService getOverrideService() {
|
||||
return goToService.getOverrideService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(ProgramLocation loc) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, Program program, Address address,
|
||||
Address refAddress) {
|
||||
ProgramLocation location = helper.getLocation(program, refAddress, address);
|
||||
return goTo(navigatable, location, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(ProgramLocation loc, Program program) {
|
||||
if (program == null || program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
showProgramFailureStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) {
|
||||
if (loc == null || loc.getAddress() == null) {
|
||||
return false;
|
||||
}
|
||||
if (program == null) {
|
||||
program = navigatable.getProgram();
|
||||
}
|
||||
if (program == null) {
|
||||
return false;
|
||||
}
|
||||
if (program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, Address goToAddress) {
|
||||
if (goToAddress == null) {
|
||||
return false;
|
||||
}
|
||||
if (navigatable == null) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
|
||||
Program program = navigatable.getProgram();
|
||||
if (program != null) {
|
||||
Memory memory = program.getMemory();
|
||||
if (!memory.contains(goToAddress)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return goTo(goToAddress, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address currentAddress, Address goToAddress) {
|
||||
if (diffGoTo(goToAddress)) {
|
||||
return true;
|
||||
}
|
||||
return goToService.goTo(currentAddress, goToAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address goToAddress) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address goToAddress, Program program) {
|
||||
if (program == null || program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
showProgramFailureStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(ExternalLocation extLoc, boolean checkNavigationOption) {
|
||||
showProgramFailureStatus();
|
||||
return false; // Can only go to locations in the Diff's second program.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
TaskMonitor monitor) {
|
||||
// Does this need to do something different here? Maybe if Diff becomes searchable?
|
||||
return goToService.goToQuery(fromAddr, queryData, listener, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
|
||||
GoToServiceListener listener, TaskMonitor monitor) {
|
||||
// Does this need to do something different here? Maybe if Diff becomes searchable?
|
||||
return goToService.goToQuery(navigatable, fromAddr, queryData, listener, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverrideService(GoToOverrideService override) {
|
||||
// Do nothing. (May need to change this later if there is reason to override Diff.)
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigatable getDefaultNavigatable() {
|
||||
return goToService.getDefaultNavigatable();
|
||||
}
|
||||
|
||||
private void showProgramFailureStatus() {
|
||||
diffPlugin.getTool().setStatusInfo(
|
||||
"Can't navigate from the Diff program to another program.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the specified program location in the right hand Diff listing.
|
||||
* @param loc go to this location
|
||||
* @return true if the listing went to the location.
|
||||
*/
|
||||
private boolean diffGoTo(ProgramLocation loc) {
|
||||
if (loc == null) {
|
||||
return false;
|
||||
}
|
||||
Address addr = loc.getAddress();
|
||||
if (addr == null) {
|
||||
return false;
|
||||
}
|
||||
saveLocation();
|
||||
boolean went = diffPlugin.getListingPanel().goTo(loc);
|
||||
saveLocation();
|
||||
return went;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the specified address in the right hand Diff listing.
|
||||
* @param addr go to this address
|
||||
* @return true if the listing went to the address.
|
||||
*/
|
||||
private boolean diffGoTo(Address addr) {
|
||||
if (addr == null) {
|
||||
return false;
|
||||
}
|
||||
saveLocation();
|
||||
boolean went = diffPlugin.getListingPanel().goTo(addr);
|
||||
saveLocation();
|
||||
return went;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving the first program's location (the left listing) in the navigation history.
|
||||
* The second program's location (the right listing) isn't saved since the navigation is
|
||||
* relative to a program in the tool's main listing. Also, if the second program's
|
||||
* locations were saved in the history, their program wouldn't be found and would cause
|
||||
* errors when restarting the application with the tool and primary program displayed, but
|
||||
* no Diff program.
|
||||
*/
|
||||
private void saveLocation() {
|
||||
Program firstProgram = diffPlugin.getFirstProgram();
|
||||
if (firstProgram == null) {
|
||||
return;
|
||||
}
|
||||
NavigationHistoryService historyService =
|
||||
diffPlugin.getTool().getService(NavigationHistoryService.class);
|
||||
if (historyService != null) {
|
||||
historyService.addNewLocation(goToService.getDefaultNavigatable());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.app.plugin.core.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffIgnoreAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Ignore for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Ignore.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffIgnoreAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Ignore", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.IGNORE));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffMergeAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Merge for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Merge if possible. Otherwise, change to Replace.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffMergeAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Merge", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.MERGE));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
/**
|
||||
* This is a navigatable for use by the right-hand listing of the Diff.
|
||||
* It should navigate within the Diff's listing, which would then reposition
|
||||
* the CodeViewer's listing.
|
||||
*/
|
||||
class DiffNavigatable implements Navigatable {
|
||||
|
||||
private ProgramDiffPlugin diffPlugin;
|
||||
private Navigatable navigatable;
|
||||
private boolean disposed = false;
|
||||
private WeakSet<NavigatableRemovalListener> navigationListeners =
|
||||
WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
|
||||
/**
|
||||
* The navigatable for the Diff. The CodeViewerService provides Diff with a listing,
|
||||
* so where appropriate this navigatable will defer to the CodeViewerService navigatable.
|
||||
* @param diffPlugin the plugin for the Diff which can be used to obtain needed info.
|
||||
* @param navigatable navigatable that the CodeViewerService provides.
|
||||
*/
|
||||
DiffNavigatable(ProgramDiffPlugin diffPlugin, Navigatable navigatable) {
|
||||
this.diffPlugin = diffPlugin;
|
||||
this.navigatable = navigatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Program program, ProgramLocation location) {
|
||||
// Defer to the CodeViewer navigatable.
|
||||
return navigatable.goTo(program, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getLocation() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return diffPlugin.getSecondProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationMemento getMemento() {
|
||||
int cursorOffset = diffPlugin.getListingPanel().getFieldPanel().getCursorOffset();
|
||||
return new CodeViewerLocationMemento(diffPlugin.getSecondProgram(),
|
||||
diffPlugin.getCurrentLocation(), cursorOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMemento(LocationMemento memento) {
|
||||
CodeViewerLocationMemento cvMemento = (CodeViewerLocationMemento) memento;
|
||||
int cursorOffset = cvMemento.getCursorOffset();
|
||||
diffPlugin.getListingPanel().getFieldPanel().positionCursor(cursorOffset);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getNavigatableIcon() {
|
||||
// Just use the CodeViewer's navigatable icon.
|
||||
return navigatable.getNavigatableIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMarkers() {
|
||||
return isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
diffPlugin.getListingPanel().getFieldPanel().requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
// Is the CodeViewer visible and the Diff visible?
|
||||
return navigatable.isVisible() && diffPlugin.isShowingDiff();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInstanceID() {
|
||||
// CodeViewer provides the listing for Diff.
|
||||
return navigatable.getInstanceID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
if (selection == null) {
|
||||
selection = new ProgramSelection();
|
||||
}
|
||||
diffPlugin.setProgram2Selection(selection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
// The right-hand Diff listing doesn't currently support highlight.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getSelection() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getHighlight() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getHighlight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNavigatableListener(NavigatableRemovalListener listener) {
|
||||
navigationListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNavigatableListener(NavigatableRemovalListener listener) {
|
||||
navigationListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
disposed = true;
|
||||
for (NavigatableRemovalListener listener : navigationListeners) {
|
||||
listener.navigatableRemoved(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHighlight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlightProvider(HighlightProvider highlightProvider, Program program) {
|
||||
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
|
||||
navigatable.setHighlightProvider(highlightProvider, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHighlightProvider(HighlightProvider highlightProvider, Program program) {
|
||||
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
|
||||
navigatable.removeHighlightProvider(highlightProvider, program);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This
|
||||
* is used to secondary views in order to install the right program.
|
||||
*/
|
||||
public class DiffProgramManager implements ProgramManager {
|
||||
ProgramDiffPlugin programDiffPlugin;
|
||||
|
||||
public DiffProgramManager(ProgramDiffPlugin programDiffPlugin) {
|
||||
this.programDiffPlugin = programDiffPlugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getCurrentProgram() {
|
||||
return programDiffPlugin.getSecondProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeOtherPrograms(boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeAllPrograms(boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeProgram() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeProgram(Program program, boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program[] getAllOpenPrograms() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram(Address addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(Program program) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(URL ghidraURL, int state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile df, int version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile, Component dialogParent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile, int version, int state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program, boolean current) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program, int state) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseProgram(Program program, Object persistentOwner) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentProgram(Program p) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPersistentOwner(Program program, Object owner) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockDown(boolean state) {
|
||||
// Not doing anything
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffReplaceAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Replace for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Replace.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffReplaceAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Replace", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.REPLACE));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.framework.plugintool.util.ServiceListener;
|
||||
|
||||
public class DiffServiceProvider implements ServiceProvider {
|
||||
|
||||
private ServiceProvider serviceProvider;
|
||||
private ProgramDiffPlugin programDiffPlugin;
|
||||
private DiffProgramManager diffProgramManager;
|
||||
private DiffGoToService diffGoToService;
|
||||
|
||||
DiffServiceProvider(ServiceProvider serviceProvider, ProgramDiffPlugin programDiffPlugin) {
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.programDiffPlugin = programDiffPlugin;
|
||||
this.diffProgramManager = new DiffProgramManager(this.programDiffPlugin);
|
||||
GoToService goToService = serviceProvider.getService(GoToService.class);
|
||||
this.diffGoToService = new DiffGoToService(goToService, programDiffPlugin);
|
||||
}
|
||||
|
||||
public void addServiceListener(ServiceListener listener) {
|
||||
serviceProvider.addServiceListener(listener);
|
||||
}
|
||||
|
||||
public <T> T getService(Class<T> serviceClass) {
|
||||
if (serviceClass == ProgramManager.class) {
|
||||
return serviceClass.cast( diffProgramManager );
|
||||
}
|
||||
else if (serviceClass == GoToService.class) {
|
||||
return serviceClass.cast( diffGoToService );
|
||||
}
|
||||
return serviceProvider.getService(serviceClass);
|
||||
}
|
||||
|
||||
public void removeServiceListener(ServiceListener listener) {
|
||||
serviceProvider.removeServiceListener(listener);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
/** A simple listener to allow signalling when a diff tasks starts and ends */
|
||||
public interface DiffTaskListener {
|
||||
|
||||
public static DiffTaskListener NULL_LISTENER = new DiffTaskListener() {
|
||||
@Override
|
||||
public void taskInProgress(boolean inProgress) {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
public void taskInProgress(boolean inProgress);
|
||||
}
|
|
@ -0,0 +1,532 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramDiffFilter;
|
||||
import ghidra.program.util.ProgramMemoryComparator;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingUtils;
|
||||
import docking.ToolTipManager;
|
||||
|
||||
/**
|
||||
* The ExecuteDiffDialog is used whenever initiating a Program Diff.
|
||||
* It allows the user to specify the types of differences to determine
|
||||
* and the address set to diff.
|
||||
*/
|
||||
public class ExecuteDiffDialog extends DialogComponentProvider {
|
||||
|
||||
public static final String DIFF_ACTION = "Diff";
|
||||
private static final String TITLE = "Determine Program Differences";
|
||||
private static final String ADDRESS_AREA_TITLE = "Address Ranges To Diff";
|
||||
|
||||
private JCheckBox diffBytesCB;
|
||||
private JCheckBox diffLabelsCB;
|
||||
private JCheckBox diffCodeUnitsCB;
|
||||
private JCheckBox diffReferencesCB;
|
||||
private JCheckBox diffProgramContextCB;
|
||||
private JCheckBox diffCommentsCB;
|
||||
private JCheckBox diffBookmarksCB;
|
||||
private JCheckBox diffPropertiesCB;
|
||||
private JCheckBox diffFunctionsCB;
|
||||
private JButton selectAllButton = new JButton("Select All");
|
||||
private JButton deselectAllButton = new JButton("Deselect All");
|
||||
private JCheckBox limitToSelectionCB;
|
||||
private JTextArea addressText;
|
||||
|
||||
private boolean diffBytes;
|
||||
private boolean diffLabels;
|
||||
private boolean diffCodeUnits;
|
||||
private boolean diffReferences;
|
||||
private boolean diffProgramContext;
|
||||
private boolean diffComments;
|
||||
private boolean diffBookmarks;
|
||||
private boolean diffProperties;
|
||||
private boolean diffFunctions;
|
||||
|
||||
private ProgramDiffFilter diffFilter;
|
||||
private JPanel diffPanel;
|
||||
private ArrayList<ActionListener> listenerList = new ArrayList<ActionListener>();
|
||||
private boolean limitToSelection;
|
||||
private AddressSetView pgm1MemorySet;
|
||||
private AddressSetView pgm1SelectionSet;
|
||||
private AddressSetView pgm1CompatibleSet;
|
||||
private boolean pgmContextEnabled = true;
|
||||
|
||||
/**
|
||||
* @param frame
|
||||
*/
|
||||
public ExecuteDiffDialog() {
|
||||
super(TITLE, true, true, true, false);
|
||||
|
||||
diffPanel = createDiffSettingsPanel();
|
||||
init();
|
||||
setHelpLocation(new HelpLocation("Diff", "ExecuteDiffDialog"));
|
||||
}
|
||||
|
||||
public void configure(Program program1, Program program2,
|
||||
AddressSetView currentProgram1SelectionSet, ProgramDiffFilter diff) {
|
||||
|
||||
this.pgm1MemorySet = program1.getMemory();
|
||||
this.pgm1SelectionSet = currentProgram1SelectionSet;
|
||||
this.pgm1CompatibleSet = ProgramMemoryComparator.getCombinedAddresses(program1, program2);
|
||||
setDiffFilter(diff);
|
||||
boolean hasSelection =
|
||||
((currentProgram1SelectionSet != null) && !currentProgram1SelectionSet.isEmpty());
|
||||
setLimitToSelectionEnabled(hasSelection);
|
||||
limitToSelection(hasSelection);
|
||||
addressText.setText(getAddressText());
|
||||
}
|
||||
|
||||
private void init() {
|
||||
addWorkPanel(diffPanel);
|
||||
addOKButton();
|
||||
setOkToolTip("Get the differences and highlight them in the second program.");
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.bean.GhidraDialog#okCallback()
|
||||
*/
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
if (!hasDiffSelection()) {
|
||||
setStatusText("At least one difference type must be checked.");
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
ActionListener listener = listenerList.get(i);
|
||||
listener.actionPerformed(new ActionEvent(this, 0, DIFF_ACTION));
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.bean.GhidraDialog#cancelCallback()
|
||||
*/
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
}
|
||||
|
||||
JPanel createDiffSettingsPanel() {
|
||||
ProgramDiffFilter diff = new ProgramDiffFilter();
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
JPanel settingsPanel = new JPanel();
|
||||
settingsPanel.add(createDiffPanel());
|
||||
panel.add(settingsPanel, BorderLayout.NORTH);
|
||||
panel.add(createAddressPanel(), BorderLayout.CENTER);
|
||||
setDiffFilter(diff);
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for all checkboxes related to applying differences.
|
||||
*/
|
||||
private JPanel createDiffPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
TitledBorder border = new TitledBorder("Do Differences On");
|
||||
panel.setBorder(border);
|
||||
panel.add(createDiffFilterPanel());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createAddressPanel() {
|
||||
JPanel addressPanel = new JPanel(new BorderLayout());
|
||||
Border border = BorderFactory.createEtchedBorder();
|
||||
addressPanel.setBorder(new TitledBorder(border, ADDRESS_AREA_TITLE));
|
||||
addressPanel.add(createLimitPanel(), BorderLayout.NORTH);
|
||||
addressText = new JTextArea(5, 30);
|
||||
addressText.setName("AddressTextArea");
|
||||
addressText.setEditable(false);
|
||||
DockingUtils.setTransparent(addressText);
|
||||
addressText.setText(getAddressText());
|
||||
JScrollPane scrolledAddresses = new JScrollPane(addressText);
|
||||
addressPanel.add(scrolledAddresses, BorderLayout.CENTER);
|
||||
|
||||
return addressPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the checkboxes to indicate the filter settings.
|
||||
*/
|
||||
private JPanel createLimitPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
limitToSelectionCB = new JCheckBox("Limit To Selection");
|
||||
limitToSelectionCB.setName("LimitToSelectionDiffCB");
|
||||
ToolTipManager.setToolTipText(limitToSelectionCB, "Limits the Diff to the selection.");
|
||||
limitToSelectionCB.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
limitToSelection = limitToSelectionCB.isSelected();
|
||||
updateDiffSetText();
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(limitToSelectionCB);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the checkboxes to indicate the filter settings.
|
||||
*/
|
||||
private JPanel createDiffFilterPanel() {
|
||||
JPanel checkBoxPanel = new JPanel();
|
||||
ToolTipManager.setToolTipText(checkBoxPanel,
|
||||
"Check the types of differences between the two "
|
||||
+ "programs that you want detected and highlighted.");
|
||||
|
||||
createBytesCheckBox();
|
||||
createLabelsCheckBox();
|
||||
createCodeUnitsCheckBox();
|
||||
createReferencesCheckBox();
|
||||
createProgramContextCheckBox();
|
||||
createCommentsCheckBox();
|
||||
createBookmarksCheckBox();
|
||||
createPropertiesCheckBox();
|
||||
createFunctionsCheckBox();
|
||||
|
||||
checkBoxPanel.setLayout(new GridLayout(3, 3, 5, 0));
|
||||
checkBoxPanel.add(diffBytesCB);
|
||||
checkBoxPanel.add(diffLabelsCB);
|
||||
checkBoxPanel.add(diffCodeUnitsCB);
|
||||
checkBoxPanel.add(diffReferencesCB);
|
||||
checkBoxPanel.add(diffProgramContextCB);
|
||||
checkBoxPanel.add(diffCommentsCB);
|
||||
checkBoxPanel.add(diffBookmarksCB);
|
||||
checkBoxPanel.add(diffPropertiesCB);
|
||||
checkBoxPanel.add(diffFunctionsCB);
|
||||
|
||||
JPanel buttonPanel = new JPanel();
|
||||
createSelectAllButton();
|
||||
buttonPanel.add(selectAllButton);
|
||||
createDeselectAllButton();
|
||||
buttonPanel.add(deselectAllButton);
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(checkBoxPanel, BorderLayout.CENTER);
|
||||
panel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void createBytesCheckBox() {
|
||||
diffBytesCB = new JCheckBox("Bytes", diffBytes);
|
||||
diffBytesCB.setName("BytesDiffCB");
|
||||
ToolTipManager.setToolTipText(diffBytesCB, "Highlight byte differences.");
|
||||
diffBytesCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffBytes = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.BYTE_DIFFS, diffBytes);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createLabelsCheckBox() {
|
||||
diffLabelsCB = new JCheckBox("Labels", diffLabels);
|
||||
diffLabelsCB.setName("LabelsDiffCB");
|
||||
ToolTipManager.setToolTipText(diffLabelsCB, "Highlight label differences.");
|
||||
diffLabelsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffLabels = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.SYMBOL_DIFFS, diffLabels);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createCodeUnitsCheckBox() {
|
||||
diffCodeUnitsCB = new JCheckBox("Code Units", diffCodeUnits);
|
||||
diffCodeUnitsCB.setName("CodeUnitsDiffCB");
|
||||
ToolTipManager.setToolTipText(diffCodeUnitsCB, "Highlight the instruction, data, "
|
||||
+ "and equate differences.");
|
||||
diffCodeUnitsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffCodeUnits = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.CODE_UNIT_DIFFS, diffCodeUnits);
|
||||
diffFilter.setFilter(ProgramDiffFilter.EQUATE_DIFFS, diffCodeUnits);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createReferencesCheckBox() {
|
||||
diffReferencesCB = new JCheckBox("References", diffReferences);
|
||||
diffReferencesCB.setName("ReferencesDiffCB");
|
||||
ToolTipManager.setToolTipText(diffReferencesCB, "Highlight the reference differences.");
|
||||
diffReferencesCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffReferences = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.REFERENCE_DIFFS, diffReferences);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createProgramContextCheckBox() {
|
||||
diffProgramContextCB = new JCheckBox("Program Context", diffProgramContext);
|
||||
diffProgramContextCB.setName("ProgramContextDiffCB");
|
||||
ToolTipManager.setToolTipText(diffProgramContextCB,
|
||||
"Highlight the program context register differences.");
|
||||
diffProgramContextCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffProgramContext = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.PROGRAM_CONTEXT_DIFFS, diffProgramContext);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createCommentsCheckBox() {
|
||||
diffCommentsCB = new JCheckBox("Comments", diffComments);
|
||||
diffCommentsCB.setName("CommentsDiffCB");
|
||||
ToolTipManager.setToolTipText(diffCommentsCB, "Highlight comment differences.");
|
||||
diffCommentsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffComments = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.COMMENT_DIFFS, diffComments);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createBookmarksCheckBox() {
|
||||
diffBookmarksCB = new JCheckBox("Bookmarks", diffBookmarks);
|
||||
diffBookmarksCB.setName("BookmarksDiffCB");
|
||||
ToolTipManager.setToolTipText(diffBookmarksCB, "Highlight bookmark differences. "
|
||||
+ "(for example, bookmark differences)");
|
||||
diffBookmarksCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffBookmarks = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.BOOKMARK_DIFFS, diffBookmarks);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createPropertiesCheckBox() {
|
||||
diffPropertiesCB = new JCheckBox("Properties", diffProperties);
|
||||
diffPropertiesCB.setName("PropertiesDiffCB");
|
||||
ToolTipManager.setToolTipText(diffPropertiesCB,
|
||||
"Highlight user defined property differences. "
|
||||
+ "(for example, Format (space) differences)");
|
||||
diffPropertiesCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffProperties = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
diffFilter.setFilter(ProgramDiffFilter.USER_DEFINED_DIFFS, diffProperties);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createFunctionsCheckBox() {
|
||||
diffFunctionsCB = new JCheckBox("Functions", diffFunctions);
|
||||
diffFunctionsCB.setName("FunctionsDiffCB");
|
||||
ToolTipManager.setToolTipText(diffFunctionsCB, "Highlight function differences.");
|
||||
diffFunctionsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
diffFunctions = (event.getStateChange() == ItemEvent.SELECTED);
|
||||
// Functions check box controls both functions and function tags.
|
||||
diffFilter.setFilter(ProgramDiffFilter.FUNCTION_DIFFS, diffFunctions);
|
||||
diffFilter.setFilter(ProgramDiffFilter.FUNCTION_TAG_DIFFS, diffFunctions);
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createSelectAllButton() {
|
||||
selectAllButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setSelectAll(true);
|
||||
}
|
||||
});
|
||||
selectAllButton.setMnemonic('S');
|
||||
}
|
||||
|
||||
private void createDeselectAllButton() {
|
||||
deselectAllButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setSelectAll(false);
|
||||
}
|
||||
});
|
||||
deselectAllButton.setMnemonic('D');
|
||||
}
|
||||
|
||||
protected void setSelectAll(boolean selected) {
|
||||
diffBytesCB.setSelected(selected);
|
||||
diffLabelsCB.setSelected(selected);
|
||||
diffCodeUnitsCB.setSelected(selected);
|
||||
diffReferencesCB.setSelected(selected);
|
||||
diffProgramContextCB.setSelected(pgmContextEnabled && selected);
|
||||
diffCommentsCB.setSelected(selected);
|
||||
diffBookmarksCB.setSelected(selected);
|
||||
diffPropertiesCB.setSelected(selected);
|
||||
diffFunctionsCB.setSelected(selected);
|
||||
}
|
||||
|
||||
private void adjustDiffFilter() {
|
||||
diffBytesCB.setSelected(diffBytes);
|
||||
diffLabelsCB.setSelected(diffLabels);
|
||||
diffCodeUnitsCB.setSelected(diffCodeUnits);
|
||||
diffReferencesCB.setSelected(diffReferences);
|
||||
diffProgramContextCB.setSelected(pgmContextEnabled && diffProgramContext);
|
||||
diffCommentsCB.setSelected(diffComments);
|
||||
diffBookmarksCB.setSelected(diffBookmarks);
|
||||
diffPropertiesCB.setSelected(diffProperties);
|
||||
diffFunctionsCB.setSelected(diffFunctions);
|
||||
|
||||
}
|
||||
|
||||
void setPgmContextEnabled(boolean enable) {
|
||||
pgmContextEnabled = enable;
|
||||
diffProgramContextCB.setEnabled(enable);
|
||||
if (!enable) {
|
||||
diffProgramContext = false;
|
||||
}
|
||||
diffProgramContextCB.setSelected(diffProgramContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the diff tool filter settings.
|
||||
* @return the current diff Filter settings.
|
||||
*/
|
||||
ProgramDiffFilter getDiffFilter() {
|
||||
return new ProgramDiffFilter(diffFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the diff tool filter settings.
|
||||
* @param filter the new diff Filter settings.
|
||||
*/
|
||||
void setDiffFilter(ProgramDiffFilter filter) {
|
||||
diffFilter = new ProgramDiffFilter(filter);
|
||||
diffBytes = diffFilter.getFilter(ProgramDiffFilter.BYTE_DIFFS);
|
||||
diffLabels = diffFilter.getFilter(ProgramDiffFilter.SYMBOL_DIFFS);
|
||||
diffCodeUnits = diffFilter.getFilter(ProgramDiffFilter.CODE_UNIT_DIFFS);
|
||||
diffReferences = diffFilter.getFilter(ProgramDiffFilter.REFERENCE_DIFFS);
|
||||
diffProgramContext =
|
||||
pgmContextEnabled && diffFilter.getFilter(ProgramDiffFilter.PROGRAM_CONTEXT_DIFFS);
|
||||
diffComments = diffFilter.getFilter(ProgramDiffFilter.COMMENT_DIFFS);
|
||||
diffBookmarks = diffFilter.getFilter(ProgramDiffFilter.BOOKMARK_DIFFS);
|
||||
diffProperties = diffFilter.getFilter(ProgramDiffFilter.USER_DEFINED_DIFFS);
|
||||
diffFunctions = diffFilter.getFilter(ProgramDiffFilter.FUNCTION_DIFFS);
|
||||
adjustDiffFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of the limitToSelection flag.
|
||||
* @return true indicates limitToSelection box is checked.
|
||||
*/
|
||||
boolean isLimitedToSelection() {
|
||||
return limitToSelectionCB.isSelected();
|
||||
}
|
||||
|
||||
void limitToSelection(boolean limit) {
|
||||
limitToSelection = limit;
|
||||
limitToSelectionCB.setSelected(limitToSelection);
|
||||
updateDiffSetText();
|
||||
}
|
||||
|
||||
void setLimitToSelectionEnabled(boolean enable) {
|
||||
limitToSelectionCB.setEnabled(enable);
|
||||
}
|
||||
|
||||
private void updateDiffSetText() {
|
||||
AddressSetView pgm1AddressSet = getAddressSet();
|
||||
addressText.setText(getAddressText());
|
||||
setOkEnabled(pgm1AddressSet != null && !pgm1AddressSet.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current address set for the diff depending on whether or not the
|
||||
* Diff is limited to a selection.
|
||||
*/
|
||||
AddressSetView getAddressSet() {
|
||||
if (isLimitedToSelection()) {
|
||||
return new AddressSet(pgm1SelectionSet);
|
||||
}
|
||||
// if (pgm1CompatibleSet.equals(pgm1MemorySet)) {
|
||||
// return null;
|
||||
// }
|
||||
return new AddressSet(pgm1CompatibleSet);
|
||||
}
|
||||
|
||||
private String getAddressText() {
|
||||
AddressSetView addrs = getAddressSet();
|
||||
StringBuffer addrStr = new StringBuffer();
|
||||
if ((addrs == null) || addrs.equals(pgm1MemorySet)) {
|
||||
return "Entire Program";
|
||||
}
|
||||
for (AddressRange range : addrs) {
|
||||
addrStr.append(range.toString() + "\n");
|
||||
}
|
||||
return addrStr.toString();
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if at least one of the checkboxes for the filter
|
||||
* has been selected.
|
||||
*/
|
||||
boolean hasDiffSelection() {
|
||||
return (diffBytes || diffLabels || diffCodeUnits || diffProgramContext || diffReferences ||
|
||||
diffComments || diffBookmarks || diffProperties || diffFunctions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if all types of differences are being determined.
|
||||
*/
|
||||
boolean isMarkingAllDiffs() {
|
||||
return (diffBytes && diffLabels && diffCodeUnits &&
|
||||
((!pgmContextEnabled) || diffProgramContext) && diffReferences && diffComments &&
|
||||
diffBookmarks && diffProperties && diffFunctions);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Command to apply diffs to current program.
|
||||
*
|
||||
*/
|
||||
class NextDiffCommand extends BackgroundCommand {
|
||||
|
||||
private ProgramDiffPlugin plugin;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param plugin
|
||||
* @param currentLocation
|
||||
* @param diffControl
|
||||
*/
|
||||
NextDiffCommand(ProgramDiffPlugin plugin) {
|
||||
super("Next Difference", false, false, true);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.cmd.BackgroundCommand#applyTo(ghidra.framework.model.DomainObject, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
monitor.setMessage("NextDiffTask starting...");
|
||||
plugin.nextDiff();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Action to save the current Diff Apply Settings as the new defaults to be used when new Diffs are started.
|
||||
*/
|
||||
class SaveApplySettingsAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Save Default Diff Apply Settings";
|
||||
private final static String GROUP_NAME = "DEFAULTS";
|
||||
private final static String DESCRIPTION = "Save Current Diff Apply Settings As The Default.";
|
||||
private DiffApplySettingsProvider settingsProvider;
|
||||
private DiffApplySettingsOptionManager settingsOptionMgr;
|
||||
|
||||
/**
|
||||
* Creates a new SaveApplySettingsAction.
|
||||
* @param settingsProvider the component provider where this action will be added.
|
||||
* @param settingsOptionMgr the options manager to save the default apply settings to.
|
||||
*/
|
||||
SaveApplySettingsAction(DiffApplySettingsProvider settingsProvider,
|
||||
DiffApplySettingsOptionManager settingsOptionMgr) {
|
||||
super(ACTION_NAME, settingsProvider.getPlugin().getName());
|
||||
this.settingsProvider = settingsProvider;
|
||||
this.settingsOptionMgr = settingsOptionMgr;
|
||||
setToolBarData(new ToolBarData(ResourceManager.loadImage("images/disk.png"), GROUP_NAME));
|
||||
setEnabled(true);
|
||||
setDescription(DESCRIPTION);
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DIFF, ACTION_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
JComponent applySettingsComponent = settingsProvider.getComponent();
|
||||
return contextObject == applySettingsComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
settingsOptionMgr.saveDefaultApplyFilter(settingsProvider.getApplyFilter());
|
||||
settingsProvider.getPlugin().getTool().setStatusInfo(
|
||||
"Diff Apply Settings have been saved to the tool as the new defaults.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* ###
|
||||
* 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.services;
|
||||
|
||||
import ghidra.app.plugin.core.diff.ProgramDiffPlugin;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Provides a service interface into the Diff Plugin for displaying
|
||||
* program differences between the current Program and another program.
|
||||
*
|
||||
*/
|
||||
@ServiceInfo(defaultProvider = ProgramDiffPlugin.class, description = "Find differences between two Programs")
|
||||
public interface DiffService {
|
||||
|
||||
/**
|
||||
* Launch the Diff dialog and display differences between the current program
|
||||
* and the otherProgram.
|
||||
* @param otherProgram a domain file for the program to Diff the current program against.
|
||||
* @return true if the second program is opened and successfully Diffed.
|
||||
*/
|
||||
public boolean launchDiff(DomainFile otherProgram);
|
||||
|
||||
/**
|
||||
* Launch the Diff dialog and display differences between the current program
|
||||
* and the otherProgram.
|
||||
* @param otherProgram the program to Diff the current program against.
|
||||
* @return true if the second program is opened and successfully Diffed.
|
||||
*/
|
||||
public boolean launchDiff(Program otherProgram);
|
||||
|
||||
/**
|
||||
* Determine if the Diff service is currently displaying a Diff.
|
||||
*/
|
||||
public boolean inProgress();
|
||||
|
||||
}
|
BIN
Ghidra/Features/ProgramDiff/src/main/resources/images/Diff16.png
Normal file
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 663 B |
BIN
Ghidra/Features/ProgramDiff/src/main/resources/images/xmag.png
Normal file
After Width: | Height: | Size: 1,000 B |
|
@ -0,0 +1,203 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.symbol.Equate;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffApply2Test extends DiffApplyTestAdapter {
|
||||
|
||||
@Test
|
||||
public void testApplyDiffsNextActionFirst() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
byte[] bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
|
||||
assertEquals((byte) 0xac, bytes[0]);
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("100"), addr("1ff"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("100");
|
||||
applyAndNext();
|
||||
|
||||
checkDiffSelection(new AddressSet(addr("00000200"), addr("000002ff")));
|
||||
assertTrue(diffPlugin.getDiffHighlightSelection().intersect(addrSet).isEmpty());
|
||||
assertEquals(addr("00000200"), getDiffAddress());
|
||||
bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
|
||||
assertEquals((byte) 0xaf, bytes[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyDiffsNextActionMiddle() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
List<Equate> eqs = diffTestP1.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(0, eqs.size());
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1002261"); // has Equate Diff
|
||||
applyAndNext();
|
||||
|
||||
checkDiffSelection(new AddressSet(addr("10022d4"), addr("10022e5")));
|
||||
assertEquals(addr("10022d4"), getDiffAddress());
|
||||
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(1, eqs.size());
|
||||
assertEquals(eqs.get(0).getName(), "uno");
|
||||
assertEquals(eqs.get(0).getValue(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyDiffsNextActionLast() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("1005e4f"), addr("1005e53"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1005e4f");
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreEntireBlock() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
// Cursor in selection
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
AddressSet addrSet = new AddressSet(addr("1002378"), addr("1002396"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1002378");
|
||||
assertNotNull(ignoreDiffs);
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
invokeLater(ignoreDiffs);
|
||||
waitForPostedSwingRunnables();
|
||||
AddressSet expectedDiffs = origDiffs.subtract(addrSet);
|
||||
ProgramSelection newSet = diffPlugin.getDiffHighlightSelection();
|
||||
assertTrue(newSet.intersect(addrSet).isEmpty());
|
||||
assertTrue(expectedDiffs.equals(newSet));
|
||||
|
||||
ProgramSelection sel = runSwing(() -> cb.getCurrentSelection());
|
||||
assertFalse(sel.isEmpty());
|
||||
|
||||
// Cursor outside selection
|
||||
origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
addrSet = new AddressSet(addr("100239d"), addr("100239d"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("100239d");
|
||||
assertNotNull(ignoreDiffs);
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
invokeLater(ignoreDiffs);
|
||||
waitForPostedSwingRunnables();
|
||||
expectedDiffs = origDiffs.subtract(addrSet);
|
||||
newSet = diffPlugin.getDiffHighlightSelection();
|
||||
assertTrue(newSet.intersect(addrSet).isEmpty());
|
||||
assertTrue(expectedDiffs.equals(newSet));
|
||||
|
||||
sel = runSwing(() -> cb.getCurrentSelection());
|
||||
assertFalse(sel.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnorePartialBlock() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
// Cursor in selection
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
AddressSet addrSet = new AddressSet(addr("1002a0b"), addr("1002a0b"));
|
||||
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("10029fe");
|
||||
assertNotNull(ignoreDiffs);
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
invokeLater(ignoreDiffs);
|
||||
waitForPostedSwingRunnables();
|
||||
AddressSet expectedDiffs = origDiffs.subtract(addrSet);
|
||||
ProgramSelection newSet = diffPlugin.getDiffHighlightSelection();
|
||||
assertTrue(newSet.intersect(addrSet).isEmpty());
|
||||
assertTrue(expectedDiffs.equals(newSet));
|
||||
|
||||
ProgramSelection sel = runSwing(() -> cb.getCurrentSelection());
|
||||
assertFalse(sel.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedo() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
List<Equate> eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(0, eqs.size());
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
|
||||
ProgramSelection newDiffs = new ProgramSelection(origDiffs.subtract(addrSet));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1002261"); // has Equate Diff
|
||||
apply();
|
||||
|
||||
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(1, eqs.size());
|
||||
assertEquals(eqs.get(0).getName(), "uno");
|
||||
assertEquals(eqs.get(0).getValue(), 1);
|
||||
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
|
||||
undo(program);
|
||||
|
||||
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(0, eqs.size());
|
||||
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection()); // For now we don't try to put diff hilights back.
|
||||
|
||||
redo(program);
|
||||
|
||||
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(1, eqs.size());
|
||||
assertEquals(eqs.get(0).getName(), "uno");
|
||||
assertEquals(eqs.get(0).getValue(), 1);
|
||||
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffApplyIgnoreTest extends DiffApplyTestAdapter {
|
||||
public DiffApplyIgnoreTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramContextIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(programContextApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002378"), addr("1002383"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteIgnore() throws Exception {
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(byteApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002b45"), addr("1002b45"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCodeUnitIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(codeUnitApplyCB);
|
||||
AddressSet as = new AddressSet(addr("10024b8"), addr("10024b8"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
memRefIgnore();
|
||||
extRefIgnore();
|
||||
stackRefIgnore();
|
||||
}
|
||||
|
||||
private void memRefIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002a2a"), addr("1002a2a"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void extRefIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1001034"), addr("1001037"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void stackRefIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("10029d1"), addr("10029d1"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
plateCommentIgnore();
|
||||
preCommentIgnore();
|
||||
eolCommentIgnore();
|
||||
repeatableCommentIgnore();
|
||||
postCommentIgnore();
|
||||
}
|
||||
|
||||
private void plateCommentIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(plateCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void preCommentIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(preCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void eolCommentIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(eolCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void repeatableCommentIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(repeatableCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void postCommentIgnore() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(postCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
ignore(labelApplyCB);
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
SymbolTable symtab = program.getSymbolTable();
|
||||
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
|
||||
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
|
||||
Arrays.sort(symbols, c);
|
||||
assertEquals(2, symbols.length);
|
||||
assertEquals("getResources", symbols[0].getName());
|
||||
assertEquals("mySymbol", symbols[1].getName());
|
||||
assertTrue(symbols[0].isPrimary());
|
||||
assertFalse(symbols[1].isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBookmarkIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(bookmarkApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002318"), addr("1002318"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
ignore(propertiesApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100248c"), addr("100248e"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionIgnore() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
AddressSet as = new AddressSet(addr("100299e"), addr("100299e"));
|
||||
assertTrue("Original diff set doesn't contain " + as.toString(), origDiffs.contains(as));
|
||||
ignore(functionApplyCB);
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.database.function.FunctionManagerDB;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
public class DiffApplyMergeTest extends DiffApplyTestAdapter {
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
|
||||
closeAllWindows();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlateCommentMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(plateCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("100415a"));
|
||||
assertEquals("This is my function for testing diff", cu.getComment(CodeUnit.PLATE_COMMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreCommentMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(preCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("1002395"));
|
||||
assertEquals("Pre: Program1\nPre: Program2", cu.getComment(CodeUnit.PRE_COMMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEolCommentMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(eolCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("100238f"));
|
||||
assertEquals("EOL: Program1\nEOL: Program2", cu.getComment(CodeUnit.EOL_COMMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatableCommentMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(repeatableCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("1002336"));
|
||||
assertEquals("ONE: Repeatable comment.\nTWO: Repeatable comment.",
|
||||
cu.getComment(CodeUnit.REPEATABLE_COMMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostCommentMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(postCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("100239d"));
|
||||
assertEquals("Post: Program1\nPost: Program2", cu.getComment(CodeUnit.POST_COMMENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can merge two different function tags that have been added to the
|
||||
* same function.
|
||||
*
|
||||
* Note: The test programs must be modified a bit to facilitate this test. Specifically
|
||||
* we have to add a function at the same address in both programs, then add tags
|
||||
* to each program, then add those tags to the newly created functions.
|
||||
*/
|
||||
@Test
|
||||
public void testFunctionTagMerge() {
|
||||
|
||||
try {
|
||||
loadProgram(diffTestP1);
|
||||
loadProgram(diffTestP2);
|
||||
|
||||
FunctionManagerDB funcMgr1 = (FunctionManagerDB) diffTestP1.getFunctionManager();
|
||||
FunctionManagerDB funcMgr2 = (FunctionManagerDB) diffTestP2.getFunctionManager();
|
||||
|
||||
// Create a function in Program 1.
|
||||
int id = diffTestP1.startTransaction("create1");
|
||||
funcMgr1.createFunction("testfunc", addr("1002040"),
|
||||
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
|
||||
diffTestP1.endTransaction(id, true);
|
||||
|
||||
// Create a function in Program 2.
|
||||
id = diffTestP2.startTransaction("create2");
|
||||
funcMgr2.createFunction("testfunc", addr("1002040"),
|
||||
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
|
||||
diffTestP2.endTransaction(id, true);
|
||||
|
||||
Function f1 = diffTestP1.getFunctionManager().getFunctionAt(addr("1002040"));
|
||||
Function f2 = diffTestP2.getFunctionManager().getFunctionAt(addr("1002040"));
|
||||
|
||||
// Create a tag and add it to Program 1.
|
||||
id = diffTestP1.startTransaction("create1");
|
||||
funcMgr1.getFunctionTagManager().createFunctionTag("TagA", "tag A comment");
|
||||
f1.addTag("TagA");
|
||||
diffTestP1.endTransaction(id, true);
|
||||
|
||||
// Create a tag and add it to Program 2.
|
||||
id = diffTestP2.startTransaction("create2");
|
||||
funcMgr2.getFunctionTagManager().createFunctionTag("TagB", "tag B comment");
|
||||
f2.addTag("TagB");
|
||||
diffTestP2.endTransaction(id, true);
|
||||
|
||||
// Open the diff display and apply the merge.
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
merge(functionTagApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002040"), addr("1002040"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
|
||||
// Check the results. We should have both tags now in the target program
|
||||
// (Program 1), so check the number of tags and make sure the names are
|
||||
// correct.
|
||||
Iterator<FunctionTag> iter = f1.getTags().iterator();
|
||||
List<String> tagNames = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
FunctionTag tag = iter.next();
|
||||
tagNames.add(tag.getName());
|
||||
}
|
||||
assertEquals(tagNames.size(), 2);
|
||||
assertTrue(tagNames.contains("TagA"));
|
||||
assertTrue(tagNames.contains("TagB"));
|
||||
|
||||
}
|
||||
catch (InvalidInputException | OverlappingFunctionException e) {
|
||||
Msg.error(this, "Error setting up function tag diff test.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelMerge() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
merge(labelApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
SymbolTable symtab = program.getSymbolTable();
|
||||
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
|
||||
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
|
||||
Arrays.sort(symbols, c);
|
||||
assertEquals(5, symbols.length);
|
||||
assertEquals("begin", symbols[0].getName());
|
||||
assertEquals("fooBar234", symbols[1].getName());
|
||||
assertEquals("getResources", symbols[2].getName());
|
||||
assertEquals("mySymbol", symbols[3].getName());
|
||||
assertEquals("sub21001", symbols[4].getName());
|
||||
assertFalse(symbols[0].isPrimary());
|
||||
assertFalse(symbols[1].isPrimary());
|
||||
assertTrue(symbols[2].isPrimary());
|
||||
assertFalse(symbols[3].isPrimary());
|
||||
assertFalse(symbols[4].isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelMergeSetPrimary() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
mergeSetPrimary(labelApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
|
||||
SymbolTable symtab = program.getSymbolTable();
|
||||
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
|
||||
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
|
||||
Arrays.sort(symbols, c);
|
||||
assertEquals(5, symbols.length);
|
||||
assertEquals("begin", symbols[0].getName());
|
||||
assertEquals("fooBar234", symbols[1].getName());
|
||||
assertEquals("getResources", symbols[2].getName());
|
||||
assertEquals("mySymbol", symbols[3].getName());
|
||||
assertEquals("sub21001", symbols[4].getName());
|
||||
assertTrue(symbols[0].isPrimary());
|
||||
assertFalse(symbols[1].isPrimary());
|
||||
assertFalse(symbols[2].isPrimary());
|
||||
assertFalse(symbols[3].isPrimary());
|
||||
assertFalse(symbols[4].isPrimary());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.database.function.FunctionManagerDB;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
public class DiffApplyReplaceTest extends DiffApplyTestAdapter {
|
||||
|
||||
public DiffApplyReplaceTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramContextReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(programContextApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002378"), addr("1002383"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(byteApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002b45"), addr("1002b45"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCodeUnitReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(codeUnitApplyCB);
|
||||
AddressSet as = new AddressSet(addr("10024b8"), addr("10024bf"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
memRefReplace();
|
||||
extRefReplace();
|
||||
stackRefReplace();
|
||||
}
|
||||
|
||||
private void memRefReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002a2a"), addr("1002a2a"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void extRefReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1001034"), addr("1001037"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void stackRefReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(refApplyCB);
|
||||
AddressSet as = new AddressSet(addr("10029d1"), addr("10029d1"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentReplace() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
plateCommentReplace();
|
||||
preCommentReplace();
|
||||
eolCommentReplace();
|
||||
repeatableCommentReplace();
|
||||
postCommentReplace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can replace tags in one function with tags from another.
|
||||
*
|
||||
* Note: The test programs must be modified a bit to facilitate this test. Specifically
|
||||
* we have to add a function at the same address in both programs, then add tags
|
||||
* to each program, then add those tags to the newly created functions.
|
||||
*/
|
||||
@Test
|
||||
public void testFunctionTagReplace() {
|
||||
|
||||
try {
|
||||
loadProgram(diffTestP1);
|
||||
loadProgram(diffTestP2);
|
||||
|
||||
FunctionManagerDB funcMgr1 = (FunctionManagerDB) diffTestP1.getFunctionManager();
|
||||
FunctionManagerDB funcMgr2 = (FunctionManagerDB) diffTestP2.getFunctionManager();
|
||||
|
||||
// Create a function in Program 1.
|
||||
int id = diffTestP1.startTransaction("create1");
|
||||
funcMgr1.createFunction("testfunc", addr("1002040"),
|
||||
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
|
||||
diffTestP1.endTransaction(id, true);
|
||||
|
||||
// Create a function in Program 2.
|
||||
id = diffTestP2.startTransaction("create2");
|
||||
funcMgr2.createFunction("testfunc", addr("1002040"),
|
||||
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
|
||||
diffTestP2.endTransaction(id, true);
|
||||
|
||||
Function f1 = diffTestP1.getFunctionManager().getFunctionAt(addr("1002040"));
|
||||
Function f2 = diffTestP2.getFunctionManager().getFunctionAt(addr("1002040"));
|
||||
|
||||
// Create a tag and add it to Program 1.
|
||||
id = diffTestP1.startTransaction("create1");
|
||||
funcMgr1.getFunctionTagManager().createFunctionTag("TagA", "tag A comment");
|
||||
funcMgr1.getFunctionTagManager().createFunctionTag("TagB", "tag B comment");
|
||||
f1.addTag("TagA");
|
||||
f1.addTag("TagB");
|
||||
diffTestP1.endTransaction(id, true);
|
||||
|
||||
// Create a tag and add it to Program 2.
|
||||
id = diffTestP2.startTransaction("create2");
|
||||
funcMgr2.getFunctionTagManager().createFunctionTag("TagC", "tag C comment");
|
||||
funcMgr2.getFunctionTagManager().createFunctionTag("TagD", "tag D comment");
|
||||
funcMgr2.getFunctionTagManager().createFunctionTag("TagE", "tag E comment");
|
||||
f2.addTag("TagC");
|
||||
f2.addTag("TagD");
|
||||
f2.addTag("TagE");
|
||||
diffTestP2.endTransaction(id, true);
|
||||
|
||||
// Open the diff display and apply the merge.
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
replace(functionTagApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002040"), addr("1002040"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
|
||||
// Check the results. We should have both tags now in the target program
|
||||
// (Program 1), so check the number of tags and make sure the names are
|
||||
// correct.
|
||||
Iterator<FunctionTag> iter = f1.getTags().iterator();
|
||||
List<String> tagNames = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
FunctionTag tag = iter.next();
|
||||
tagNames.add(tag.getName());
|
||||
}
|
||||
assertEquals(tagNames.size(), 3);
|
||||
assertTrue(tagNames.contains("TagC"));
|
||||
assertTrue(tagNames.contains("TagD"));
|
||||
assertTrue(tagNames.contains("TagE"));
|
||||
|
||||
}
|
||||
catch (InvalidInputException | OverlappingFunctionException e) {
|
||||
Msg.error(this, "Error setting up function tag diff test.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void plateCommentReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(plateCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void preCommentReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(preCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void eolCommentReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(eolCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void repeatableCommentReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(repeatableCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
private void postCommentReplace() {
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(postCommentApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelReplace() {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(labelApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
SymbolTable symtab = program.getSymbolTable();
|
||||
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
|
||||
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
|
||||
Arrays.sort(symbols, c);
|
||||
assertEquals(3, symbols.length);
|
||||
assertEquals("begin", symbols[0].getName());
|
||||
assertEquals("fooBar234", symbols[1].getName());
|
||||
assertEquals("sub21001", symbols[2].getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBookmarkReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(bookmarkApplyCB);
|
||||
AddressSet as = new AddressSet(addr("1002318"), addr("1002318"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
replace(propertiesApplyCB);
|
||||
AddressSet as = new AddressSet(addr("100248c"), addr("100248e"));
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionReplace() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
|
||||
AddressSet as = new AddressSet(addr("100299e"), addr("100299e"));
|
||||
assertTrue("Original diff set doesn't contain " + as.toString(), origDiffs.contains(as));
|
||||
replace(functionApplyCB);
|
||||
setDiffSelection(as);
|
||||
apply();
|
||||
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.util.DiffUtility;
|
||||
|
||||
public class DiffApplyTest extends DiffApplyTestAdapter {
|
||||
|
||||
@Test
|
||||
public void testShowHideDiffApplySettings() {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
|
||||
diffPlugin.getDiffApplySettingsProvider().closeComponent();
|
||||
waitForPostedSwingRunnables();
|
||||
assertTrue(!isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
|
||||
invokeLater(diffApplySettings);
|
||||
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
|
||||
diffPlugin.getDiffApplySettingsProvider().closeComponent();
|
||||
waitForPostedSwingRunnables();
|
||||
assertTrue(!isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
|
||||
invokeLater(diffApplySettings);
|
||||
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialDiffApplySettings() {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
isReplace(programContextApplyCB);
|
||||
isReplace(byteApplyCB);
|
||||
isReplace(codeUnitApplyCB);
|
||||
isReplace(refApplyCB);
|
||||
isMerge(plateCommentApplyCB);
|
||||
isMerge(preCommentApplyCB);
|
||||
isMerge(eolCommentApplyCB);
|
||||
isMerge(repeatableCommentApplyCB);
|
||||
isMerge(postCommentApplyCB);
|
||||
isMergeSetPrimary(labelApplyCB);
|
||||
isReplace(functionApplyCB);
|
||||
isReplace(bookmarkApplyCB);
|
||||
isReplace(propertiesApplyCB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreAllDiffsSettings() {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(ignoreAll);
|
||||
isIgnore(programContextApplyCB);
|
||||
isIgnore(byteApplyCB);
|
||||
isIgnore(codeUnitApplyCB);
|
||||
isIgnore(refApplyCB);
|
||||
isIgnore(plateCommentApplyCB);
|
||||
isIgnore(preCommentApplyCB);
|
||||
isIgnore(eolCommentApplyCB);
|
||||
isIgnore(repeatableCommentApplyCB);
|
||||
isIgnore(postCommentApplyCB);
|
||||
isIgnore(labelApplyCB);
|
||||
isIgnore(functionApplyCB);
|
||||
isIgnore(bookmarkApplyCB);
|
||||
isIgnore(propertiesApplyCB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAllDiffsSettings() {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(replaceAll);
|
||||
isReplace(programContextApplyCB);
|
||||
isReplace(byteApplyCB);
|
||||
isReplace(codeUnitApplyCB);
|
||||
isReplace(refApplyCB);
|
||||
isReplace(plateCommentApplyCB);
|
||||
isReplace(preCommentApplyCB);
|
||||
isReplace(eolCommentApplyCB);
|
||||
isReplace(repeatableCommentApplyCB);
|
||||
isReplace(postCommentApplyCB);
|
||||
isReplace(labelApplyCB);
|
||||
isReplace(functionApplyCB);
|
||||
isReplace(bookmarkApplyCB);
|
||||
isReplace(propertiesApplyCB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeAllDiffsSettings() {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(ignoreAll);
|
||||
invokeLater(mergeAll);
|
||||
isReplace(programContextApplyCB);
|
||||
isReplace(byteApplyCB);
|
||||
isReplace(codeUnitApplyCB);
|
||||
isReplace(refApplyCB);
|
||||
isMerge(plateCommentApplyCB);
|
||||
isMerge(preCommentApplyCB);
|
||||
isMerge(eolCommentApplyCB);
|
||||
isMerge(repeatableCommentApplyCB);
|
||||
isMerge(postCommentApplyCB);
|
||||
isMergeSetPrimary(labelApplyCB);
|
||||
isReplace(functionApplyCB);
|
||||
isReplace(bookmarkApplyCB);
|
||||
isReplace(propertiesApplyCB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyAllDiffsActionWithSimpleLabelMerge() throws Exception {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
merge(labelApplyCB);
|
||||
|
||||
invokeLater(selectAllDiffs);
|
||||
apply();
|
||||
|
||||
// FUTURE: Check that actually applied.
|
||||
|
||||
AddressSet addrSet = new AddressSet();
|
||||
// Comment Diffs
|
||||
addrSet.addRange(addr("1002040"), addr("1002040"));
|
||||
addrSet.addRange(addr("1002304"), addr("1002304"));
|
||||
addrSet.addRange(addr("1002306"), addr("1002306"));
|
||||
addrSet.addRange(addr("100230b"), addr("100230b"));
|
||||
addrSet.addRange(addr("1002312"), addr("1002312"));
|
||||
addrSet.addRange(addr("1002336"), addr("1002336"));
|
||||
addrSet.addRange(addr("1002346"), addr("1002346"));
|
||||
addrSet.addRange(addr("100238f"), addr("100238f"));
|
||||
addrSet.addRange(addr("1002395"), addr("1002395"));
|
||||
addrSet.addRange(addr("100239d"), addr("100239d"));
|
||||
addrSet.addRange(addr("10030d2"), addr("10030d2"));
|
||||
addrSet.addRange(addr("100355f"), addr("100355f"));
|
||||
|
||||
// Label Diffs
|
||||
addrSet.addRange(addr("1002a01"), addr("1002a01"));
|
||||
addrSet.addRange(addr("1002a0c"), addr("1002a0c"));
|
||||
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
|
||||
|
||||
// onlyInProgram1
|
||||
addrSet.addRange(addr("00000200"), addr("000002ff"));
|
||||
|
||||
// Conflicting Data Diffs
|
||||
addrSet.add(getPgmConflictDataDiffs());
|
||||
|
||||
checkDiffSelection(DiffUtility.getCodeUnitSet(addrSet, program));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyAllDiffsAction() throws Exception { // Merges labels and sets primary.
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(selectAllDiffs);
|
||||
apply();
|
||||
|
||||
// FUTURE: Check that actually applied.
|
||||
|
||||
AddressSet addrSet = new AddressSet();
|
||||
// Comment Diffs
|
||||
addrSet.addRange(addr("1002040"), addr("1002040"));
|
||||
addrSet.addRange(addr("1002304"), addr("1002304"));
|
||||
addrSet.addRange(addr("1002306"), addr("1002306"));
|
||||
addrSet.addRange(addr("100230b"), addr("100230b"));
|
||||
addrSet.addRange(addr("1002312"), addr("1002312"));
|
||||
addrSet.addRange(addr("1002336"), addr("1002336"));
|
||||
addrSet.addRange(addr("1002346"), addr("1002346"));
|
||||
addrSet.addRange(addr("100238f"), addr("100238f"));
|
||||
addrSet.addRange(addr("1002395"), addr("1002395"));
|
||||
addrSet.addRange(addr("100239d"), addr("100239d"));
|
||||
addrSet.addRange(addr("10030d2"), addr("10030d2"));
|
||||
addrSet.addRange(addr("100355f"), addr("100355f"));
|
||||
|
||||
// Label Diffs
|
||||
addrSet.addRange(addr("1002a01"), addr("1002a01"));
|
||||
addrSet.addRange(addr("1002a0c"), addr("1002a0c"));
|
||||
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
|
||||
|
||||
// onlyInProgram1
|
||||
addrSet.addRange(addr("00000200"), addr("000002ff"));
|
||||
|
||||
// Conflicting Data Diffs
|
||||
addrSet.add(getPgmConflictDataDiffs());
|
||||
|
||||
checkDiffSelection(DiffUtility.getCodeUnitSet(addrSet, program));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAllDiffsAction() throws Exception {
|
||||
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
|
||||
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(replaceAll);
|
||||
invokeLater(selectAllDiffs);
|
||||
apply();
|
||||
|
||||
checkDiffSelection(getPgmConflictDataDiffs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAllDiffsLabelConflict() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
waitForPostedSwingRunnables();
|
||||
showApplySettings();
|
||||
|
||||
invokeLater(replaceAll);
|
||||
invokeLater(selectAllDiffs);
|
||||
apply();
|
||||
|
||||
checkDiffSelection(getSetupConflictDataDiffs());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffApplyTestAdapter extends DiffTestAdapter {
|
||||
DockingActionIf ignoreAll;
|
||||
DockingActionIf replaceAll;
|
||||
DockingActionIf mergeAll;
|
||||
|
||||
JComponent settingsPanel;
|
||||
JComboBox<?> programContextApplyCB;
|
||||
JComboBox<?> byteApplyCB;
|
||||
JComboBox<?> codeUnitApplyCB;
|
||||
JComboBox<?> refApplyCB;
|
||||
JComboBox<?> plateCommentApplyCB;
|
||||
JComboBox<?> preCommentApplyCB;
|
||||
JComboBox<?> eolCommentApplyCB;
|
||||
JComboBox<?> repeatableCommentApplyCB;
|
||||
JComboBox<?> postCommentApplyCB;
|
||||
JComboBox<?> labelApplyCB;
|
||||
JComboBox<?> functionApplyCB;
|
||||
JComboBox<?> bookmarkApplyCB;
|
||||
JComboBox<?> propertiesApplyCB;
|
||||
JComboBox<?> functionTagApplyCB;
|
||||
|
||||
public DiffApplyTestAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicated combo box selection to "Ignore".
|
||||
* @param comboBox the combo box
|
||||
*/
|
||||
void ignore(final JComboBox<?> comboBox) {
|
||||
runSwing(() -> {
|
||||
ComboBoxModel<?> model = comboBox.getModel();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
if (model.getElementAt(i).toString().equals("Ignore")) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
isIgnore(comboBox);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicated combo box selection to "Replace".
|
||||
* @param comboBox the combo box
|
||||
*/
|
||||
void replace(final JComboBox<?> comboBox) {
|
||||
runSwing(() -> {
|
||||
ComboBoxModel<?> model = comboBox.getModel();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
if (model.getElementAt(i).toString().equals("Replace")) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
isReplace(comboBox);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicated combo box selection to "Merge".
|
||||
* @param comboBox the combo box
|
||||
*/
|
||||
void merge(final JComboBox<?> comboBox) {
|
||||
runSwing(() -> {
|
||||
ComboBoxModel<?> model = comboBox.getModel();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
if (model.getElementAt(i).toString().equals("Merge")) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
isMerge(comboBox);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicated combo box selection to "Merge & Set Primary".
|
||||
* @param comboBox the combo box
|
||||
*/
|
||||
void mergeSetPrimary(final JComboBox<?> comboBox) {
|
||||
runSwing(() -> {
|
||||
ComboBoxModel<?> model = comboBox.getModel();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
if (model.getElementAt(i).toString().equals("Merge & Set Primary")) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
isMergeSetPrimary(comboBox);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comboBox the ComboBox for the Diff Apply Setting to check
|
||||
*/
|
||||
void isIgnore(JComboBox<?> comboBox) {
|
||||
assertEquals(comboBox.getName(), "Ignore", comboBox.getSelectedItem().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comboBox the ComboBox for the Diff Apply Setting to check
|
||||
*/
|
||||
void isReplace(JComboBox<?> comboBox) {
|
||||
assertEquals(comboBox.getName(), "Replace", comboBox.getSelectedItem().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comboBox the ComboBox for the Diff Apply Setting to check
|
||||
*/
|
||||
void isMerge(JComboBox<?> comboBox) {
|
||||
assertEquals(comboBox.getName(), "Merge", comboBox.getSelectedItem().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comboBox the ComboBox for the Diff Apply Setting to check
|
||||
*/
|
||||
void isMergeSetPrimary(JComboBox<?> comboBox) {
|
||||
assertEquals(comboBox.getName(), "Merge & Set Primary",
|
||||
comboBox.getSelectedItem().toString());
|
||||
}
|
||||
|
||||
void waitForApply() {
|
||||
waitForCondition(() -> getWindow("Apply Differences") == null);
|
||||
}
|
||||
|
||||
void waitForIgnore() {
|
||||
waitForCondition(() -> getWindow("Ignore Differences") == null);
|
||||
}
|
||||
|
||||
void apply() {
|
||||
invokeLater(applyDiffs);
|
||||
waitForSwing();
|
||||
waitForApply();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
void applyAndNext() {
|
||||
invokeLater(applyDiffsNext);
|
||||
waitForSwing();
|
||||
waitForApply();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
void ignoreAndNext() {
|
||||
invokeLater(ignoreDiffs);
|
||||
waitForSwing();
|
||||
waitForIgnore();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
void showApplySettings() {
|
||||
invokeLater(diffApplySettings);
|
||||
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
|
||||
settingsPanel =
|
||||
(JComponent) findComponentByName(tool.getToolFrame(), "Diff Apply Settings Panel");
|
||||
assertNotNull(settingsPanel);
|
||||
|
||||
getApplySettingsActions();
|
||||
getApplySettingsComboBoxes();
|
||||
}
|
||||
|
||||
void getApplySettingsActions() {
|
||||
ignoreAll = getAction(diffPlugin, "Set All To Ignore");
|
||||
replaceAll = getAction(diffPlugin, "Set All To Replace");
|
||||
mergeAll = getAction(diffPlugin, "Set All To Merge");
|
||||
}
|
||||
|
||||
void getApplySettingsComboBoxes() {
|
||||
programContextApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Program Context Diff Apply CB");
|
||||
byteApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "Bytes Diff Apply CB");
|
||||
codeUnitApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Code Units Diff Apply CB");
|
||||
refApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "References Diff Apply CB");
|
||||
plateCommentApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Plate Comments Diff Apply CB");
|
||||
preCommentApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Pre Comments Diff Apply CB");
|
||||
eolCommentApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Eol Comments Diff Apply CB");
|
||||
repeatableCommentApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Repeatable Comments Diff Apply CB");
|
||||
postCommentApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Post Comments Diff Apply CB");
|
||||
labelApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "Labels Diff Apply CB");
|
||||
functionApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Functions Diff Apply CB");
|
||||
bookmarkApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Bookmarks Diff Apply CB");
|
||||
propertiesApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Properties Diff Apply CB");
|
||||
functionTagApplyCB =
|
||||
(JComboBox<?>) findComponentByName(settingsPanel, "Function Tags Diff Apply CB");
|
||||
}
|
||||
|
||||
void checkDiffSelection(AddressSetView addrSet) {
|
||||
ProgramSelection expectedSelection = new ProgramSelection(addrSet);
|
||||
ProgramSelection currentSelection = cb.getCurrentSelection();
|
||||
AddressSet missingFromSelection = expectedSelection.subtract(currentSelection);
|
||||
AddressSet unexpectedlySelected = currentSelection.subtract(expectedSelection);
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if (!missingFromSelection.isEmpty()) {
|
||||
buf.append("\nSelection expected the following addresses but they are missing: \n" +
|
||||
missingFromSelection.toString());
|
||||
}
|
||||
if (!unexpectedlySelected.isEmpty()) {
|
||||
buf.append("\nSelection unexpectedly contains the following addresses: \n" +
|
||||
unexpectedlySelected.toString());
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
String message = buf.toString();
|
||||
Assert.fail(message);
|
||||
}
|
||||
assertEquals(expectedSelection, currentSelection);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void checkAddressArrays(String type, Address[] expectedAddresses,
|
||||
Address[] actualAddresses) {
|
||||
Arrays.sort(expectedAddresses);
|
||||
Arrays.sort(actualAddresses);
|
||||
ArrayList<Address> extraList = new ArrayList<>();
|
||||
ArrayList<Address> missingList = new ArrayList<>();
|
||||
int expectedIndex = 0, actualIndex = 0;
|
||||
int expectedLength = expectedAddresses.length;
|
||||
int actualLength = actualAddresses.length;
|
||||
while ((expectedIndex < expectedLength) && (actualIndex < actualLength)) {
|
||||
Address expectedAddress = expectedAddresses[expectedIndex];
|
||||
Address actualAddress = actualAddresses[actualIndex];
|
||||
int compareExpectedToActual = expectedAddress.compareTo(actualAddress);
|
||||
if (compareExpectedToActual == 0) {
|
||||
expectedIndex++;
|
||||
actualIndex++;
|
||||
}
|
||||
else if (compareExpectedToActual < 0) {
|
||||
missingList.add(expectedAddress);
|
||||
expectedIndex++;
|
||||
}
|
||||
else {
|
||||
extraList.add(actualAddress);
|
||||
actualIndex++;
|
||||
}
|
||||
}
|
||||
while (expectedIndex < expectedLength) {
|
||||
missingList.add(expectedAddresses[expectedIndex]);
|
||||
expectedIndex++;
|
||||
}
|
||||
while (actualIndex < actualLength) {
|
||||
extraList.add(actualAddresses[actualIndex]);
|
||||
actualIndex++;
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if (!missingList.isEmpty()) {
|
||||
buf.append(type + "s are missing at ");
|
||||
buf.append(missingList.get(0).toString());
|
||||
int numMissing = missingList.size();
|
||||
for (int index = 1; index < numMissing; index++) {
|
||||
buf.append(", " + missingList.get(index).toString());
|
||||
}
|
||||
buf.append(".\n");
|
||||
}
|
||||
if (!extraList.isEmpty()) {
|
||||
buf.append("Unexpectedly found " + type + "s at ");
|
||||
buf.append(extraList.get(0).toString());
|
||||
int numMissing = extraList.size();
|
||||
for (int index = 1; index < numMissing; index++) {
|
||||
buf.append(", " + extraList.get(index).toString());
|
||||
}
|
||||
buf.append(".\n");
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
String message = buf.toString();
|
||||
// System.out.println(message);
|
||||
Assert.fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkProgramSelection(AddressSetView addrSet) {
|
||||
assertEquals(new ProgramSelection(addrSet), cb.getCurrentSelection());
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDiffSelection(final AddressSetView addrSet) {
|
||||
runSwing(() -> diffPlugin.setProgram2Selection(new ProgramSelection(addrSet)), true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffEnablementTest extends DiffTestAdapter {
|
||||
|
||||
public DiffEnablementTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
// programBuilderDiffTest1.createComment("0x1001010", "Hey", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSelectionDiffEnablement() {
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(!applyDiffs.isEnabled());
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
assertTrue(!ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(!prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(!setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectFirstDiffEnablement() {
|
||||
ProgramSelection sel = new ProgramSelection(addr("100"), addr("110"));
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("100")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(!prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectLastDiffEnablement() {
|
||||
ProgramSelection sel = new ProgramSelection(addr("1005e4f"), addr("1004e53"));
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("1005e4f")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(!nextDiff.isEnabled());
|
||||
assertTrue(prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectInnerDiffBlockEnablement() {
|
||||
ProgramSelection sel = new ProgramSelection(addr("10018ce"), addr("10018cf"));
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("1002347")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectPartialDiffBlockEnablement() {
|
||||
ProgramSelection sel = new ProgramSelection(addr("100233f"), addr("2345"));
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("1002345")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectMultipleDiffBlockEnablement() {
|
||||
AddressSet as = new AddressSet(addr("10018ce"), addr("10018cf"));
|
||||
as.addRange(addr("1002040"), addr("1002042"));
|
||||
as.addRange(addr("100233c"), addr("1002347"));
|
||||
|
||||
ProgramSelection sel = new ProgramSelection(as);
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("1002040")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectAllDiffsEnablement() {
|
||||
ProgramSelection sel = new ProgramSelection(addr("1001000"), addr("100f400"));
|
||||
setDiffSelection(sel);
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
|
||||
addr("1002040")), program));
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(applyDiffs.isEnabled());
|
||||
assertTrue(applyDiffsNext.isEnabled());
|
||||
assertTrue(ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DiffGet2Test extends DiffTestAdapter {
|
||||
|
||||
public DiffGet2Test() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
// programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
byteCB.setSelected(true);
|
||||
waitForPostedSwingRunnables();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(getPgmByteDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCodeUnitDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
codeUnitCB.setSelected(true);
|
||||
waitForPostedSwingRunnables();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(getPgmCodeUnitDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProgramContextDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
programContextCB.setSelected(true);
|
||||
waitForPostedSwingRunnables();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
waitForDiff();
|
||||
assertEquals(getPgmProgramContextDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBookmarkDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
bookmarkCB.setSelected(true);
|
||||
waitForPostedSwingRunnables();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
waitForDiff();
|
||||
assertEquals(getPgmBookmarkDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommentDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
commentCB.setSelected(true);
|
||||
waitForPostedSwingRunnables();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
waitForDiff();
|
||||
assertEquals(getPgmCommentDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,373 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Window;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.MultiLineLabel;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffGetTest extends DiffTestAdapter {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffAgainstSelf() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP1);
|
||||
|
||||
assertTrue(programContextCB.isSelected());
|
||||
assertTrue(byteCB.isSelected());
|
||||
assertTrue(codeUnitCB.isSelected());
|
||||
assertTrue(refCB.isSelected());
|
||||
assertTrue(commentCB.isSelected());
|
||||
assertTrue(labelCB.isSelected());
|
||||
assertTrue(functionCB.isSelected());
|
||||
assertTrue(bookmarkCB.isSelected());
|
||||
assertTrue(propertiesCB.isSelected());
|
||||
|
||||
assertTrue(!limitToSelectionCB.isSelected());
|
||||
assertEquals("Entire Program", limitText.getText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(new ProgramSelection(), diffPlugin.getDiffHighlightSelection());
|
||||
Window w = waitForWindow("No Differences");
|
||||
close(w);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffNoTypes() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
|
||||
assertTrue(!programContextCB.isSelected());
|
||||
assertTrue(!byteCB.isSelected());
|
||||
assertTrue(!codeUnitCB.isSelected());
|
||||
assertTrue(!refCB.isSelected());
|
||||
assertTrue(!commentCB.isSelected());
|
||||
assertTrue(!labelCB.isSelected());
|
||||
assertTrue(!functionCB.isSelected());
|
||||
assertTrue(!bookmarkCB.isSelected());
|
||||
assertTrue(!propertiesCB.isSelected());
|
||||
|
||||
assertTrue(!limitToSelectionCB.isSelected());
|
||||
assertEquals("Entire Program", limitText.getText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
|
||||
assertNotNull(getDiffsDialog);
|
||||
JLabel statusLabel = (JLabel) findComponentByName(getDiffsDialog, "statusLabel");
|
||||
assertEquals("At least one difference type must be checked.", statusLabel.getText());
|
||||
assertEquals(new ProgramSelection(), diffPlugin.getDiffHighlightSelection());
|
||||
close(getDiffsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
|
||||
assertTrue(programContextCB.isSelected());
|
||||
assertTrue(byteCB.isSelected());
|
||||
assertTrue(codeUnitCB.isSelected());
|
||||
assertTrue(refCB.isSelected());
|
||||
assertTrue(commentCB.isSelected());
|
||||
assertTrue(labelCB.isSelected());
|
||||
assertTrue(functionCB.isSelected());
|
||||
assertTrue(bookmarkCB.isSelected());
|
||||
assertTrue(propertiesCB.isSelected());
|
||||
|
||||
assertTrue(!limitToSelectionCB.isSelected());
|
||||
assertEquals("Entire Program", limitText.getText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
waitForSwing();
|
||||
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepGetDiffsCheckboxState() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
setCheckBoxes(true, new JCheckBox[] { propertiesCB, commentCB });
|
||||
|
||||
assertTrue(!programContextCB.isSelected());
|
||||
assertTrue(!byteCB.isSelected());
|
||||
assertTrue(!codeUnitCB.isSelected());
|
||||
assertTrue(!refCB.isSelected());
|
||||
assertTrue(commentCB.isSelected());
|
||||
assertTrue(!labelCB.isSelected());
|
||||
assertTrue(!functionCB.isSelected());
|
||||
assertTrue(!bookmarkCB.isSelected());
|
||||
assertTrue(propertiesCB.isSelected());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
AddressSet as = getSetupCommentDiffs().union(getSetupPropertyDiffs());
|
||||
assertEquals(new ProgramSelection(as), diffPlugin.getDiffHighlightSelection());
|
||||
|
||||
invokeLater(getDiffs);
|
||||
|
||||
assertTrue(!programContextCB.isSelected());
|
||||
assertTrue(!byteCB.isSelected());
|
||||
assertTrue(!codeUnitCB.isSelected());
|
||||
assertTrue(!refCB.isSelected());
|
||||
assertTrue(commentCB.isSelected());
|
||||
assertTrue(!labelCB.isSelected());
|
||||
assertTrue(!functionCB.isSelected());
|
||||
assertTrue(!bookmarkCB.isSelected());
|
||||
assertTrue(propertiesCB.isSelected());
|
||||
|
||||
setCheckBoxes(true, new JCheckBox[] { refCB });
|
||||
as = as.union(getSetupReferenceDiffs());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
assertEquals(new ProgramSelection(as), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionAllDiffs() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
AddressSet selectionSet = new AddressSet(addr("1001708"), addr("1003001"));
|
||||
tool.firePluginEvent(
|
||||
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
|
||||
|
||||
invokeLater(getDiffs);
|
||||
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
|
||||
assertNotNull(getDiffsDialog);
|
||||
getDiffDialogComponents(getDiffsDialog.getComponent());
|
||||
|
||||
assertTrue(limitToSelectionCB.isSelected());
|
||||
assertEquals("[01001708, 01003001]\n", limitText.getText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(getSetupAllDiffsSet().intersect(selectionSet),
|
||||
diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeselectAndSelectAllTypesOfDiffs() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
// AddressSet selectionSet = new AddressSet(addrFactory, addr("1001708"), addr("1003001"));
|
||||
// tool.firePluginEvent(new ProgramSelectionPluginEvent("test",
|
||||
// new ProgramSelection(selectionSet), program));
|
||||
|
||||
invokeLater(getDiffs);
|
||||
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
|
||||
assertNotNull(getDiffsDialog);
|
||||
getDiffDialogComponents(getDiffsDialog.getComponent());
|
||||
|
||||
assertEquals(true, programContextCB.isSelected());
|
||||
assertEquals(true, byteCB.isSelected());
|
||||
assertEquals(true, codeUnitCB.isSelected());
|
||||
assertEquals(true, refCB.isSelected());
|
||||
assertEquals(true, commentCB.isSelected());
|
||||
assertEquals(true, labelCB.isSelected());
|
||||
assertEquals(true, functionCB.isSelected());
|
||||
assertEquals(true, bookmarkCB.isSelected());
|
||||
assertEquals(true, propertiesCB.isSelected());
|
||||
assertEquals(false, limitToSelectionCB.isSelected());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "Deselect All");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(false, programContextCB.isSelected());
|
||||
assertEquals(false, byteCB.isSelected());
|
||||
assertEquals(false, codeUnitCB.isSelected());
|
||||
assertEquals(false, refCB.isSelected());
|
||||
assertEquals(false, commentCB.isSelected());
|
||||
assertEquals(false, labelCB.isSelected());
|
||||
assertEquals(false, functionCB.isSelected());
|
||||
assertEquals(false, bookmarkCB.isSelected());
|
||||
assertEquals(false, propertiesCB.isSelected());
|
||||
assertEquals(false, limitToSelectionCB.isSelected());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
assertEquals("At least one difference type must be checked.",
|
||||
getDiffsDialog.getStatusText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "Select All");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(true, programContextCB.isSelected());
|
||||
assertEquals(true, byteCB.isSelected());
|
||||
assertEquals(true, codeUnitCB.isSelected());
|
||||
assertEquals(true, refCB.isSelected());
|
||||
assertEquals(true, commentCB.isSelected());
|
||||
assertEquals(true, labelCB.isSelected());
|
||||
assertEquals(true, functionCB.isSelected());
|
||||
assertEquals(true, bookmarkCB.isSelected());
|
||||
assertEquals(true, propertiesCB.isSelected());
|
||||
assertEquals(false, limitToSelectionCB.isSelected());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
waitForSwing(); // Wait for Diff Highlight to get displayed.
|
||||
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionUnchecked() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
AddressSet selectionSet = new AddressSet(addr("1001708"), addr("1003001"));
|
||||
tool.firePluginEvent(
|
||||
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
|
||||
|
||||
invokeLater(getDiffs);
|
||||
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
|
||||
assertNotNull(getDiffsDialog);
|
||||
getDiffDialogComponents(getDiffsDialog.getComponent());
|
||||
|
||||
assertTrue(limitToSelectionCB.isSelected());
|
||||
assertEquals("[01001708, 01003001]\n", limitText.getText());
|
||||
|
||||
SwingUtilities.invokeLater(() -> limitToSelectionCB.doClick());
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(!limitToSelectionCB.isSelected());
|
||||
assertEquals("Entire Program", limitText.getText());
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionLabelDiffs() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
AddressSet selectionSet = new AddressSet(addr("1006202"), addr("1006400"));
|
||||
tool.firePluginEvent(
|
||||
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
|
||||
|
||||
invokeLater(getDiffs);
|
||||
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
|
||||
assertNotNull(getDiffsDialog);
|
||||
getDiffDialogComponents(getDiffsDialog.getComponent());
|
||||
|
||||
setAllTypes(false);
|
||||
setToggleButtonSelected(labelCB, true);
|
||||
assertTrue(limitToSelectionCB.isSelected());
|
||||
assertEquals("[01006202, 01006400]\n", limitText.getText());
|
||||
waitForSwing();
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(getSetupLabelDiffs().intersect(selectionSet),
|
||||
diffPlugin.getDiffHighlightSelection());
|
||||
Window window = waitForWindow("No Differences In Selection");
|
||||
close(window);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetReferenceDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
setToggleButtonSelected(refCB, true);
|
||||
waitForSwing();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
assertEquals(getSetupReferenceDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLabelDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
setToggleButtonSelected(labelCB, true);
|
||||
waitForSwing();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
assertEquals(getSetupLabelDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFunctionDiffsAction() throws Exception {
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
setToggleButtonSelected(functionCB, true);
|
||||
waitForSwing();
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
assertEquals(getSetupFunctionDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPropertyDiffsAction() throws Exception {
|
||||
|
||||
getDiffDialog(diffTestP1, diffTestP2);
|
||||
setAllTypes(false);
|
||||
|
||||
setToggleButtonSelected(propertiesCB, true);
|
||||
waitForSwing();
|
||||
|
||||
pressButtonByText(getDiffsDialog, "OK");
|
||||
waitForDiff();
|
||||
|
||||
assertEquals(getSetupPropertyDiffs(), diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentLanguages() throws Exception {
|
||||
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
pickSecondProgram(getSparcProgram());
|
||||
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
Window win = waitForWindow("Can't Open Selected Program");
|
||||
assertNotNull(win);
|
||||
String msg = findComponent(win, MultiLineLabel.class).getLabel();
|
||||
assertTrue(msg.startsWith("Programs languages don't match."));
|
||||
pressButton(win, "OK");
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
pressButton(win, "Cancel");
|
||||
assertFalse(win.isShowing());
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
}
|
||||
|
||||
private Program getSparcProgram() throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder("Sparc", ProgramBuilder._SPARC64);
|
||||
builder.createMemory("test", "0x100", 0x1000);
|
||||
return builder.getProgram();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.symbol.Equate;
|
||||
|
||||
/**
|
||||
* Tests the Ignore function of the Diff Tool, such that the current
|
||||
* difference is ignored and the next difference is selected.
|
||||
*/
|
||||
public class DiffIgnoreTest extends DiffApplyTestAdapter {
|
||||
public DiffIgnoreTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a difference is ignored and the next difference is selected
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testIgnoreDiffsNextActionFirst() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
|
||||
byte[] bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
|
||||
assertEquals((byte) 0xac, bytes[0]);
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("100"), addr("1ff"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("100");
|
||||
ignoreAndNext();
|
||||
|
||||
checkDiffSelection(new AddressSet(addr("00000200"), addr("000002ff")));
|
||||
assertTrue(diffPlugin.getDiffHighlightSelection().intersect(addrSet).isEmpty());
|
||||
assertEquals(addr("00000200"), getDiffAddress());
|
||||
bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
|
||||
assertEquals((byte) 0xac, bytes[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if Equate Tables are properly ignored and the next difference is properly selected
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testIgnoreDiffsNextActionMiddle() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
|
||||
List<Equate> eqs = diffTestP1.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(0, eqs.size());
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1002261"); // has Equate Diff
|
||||
ignoreAndNext();
|
||||
|
||||
checkDiffSelection(new AddressSet(addr("10022d4"), addr("10022e5")));
|
||||
assertEquals(addr("10022d4"), getDiffAddress());
|
||||
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
|
||||
assertEquals(0, eqs.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the ignore button is disabled after ignoring the last difference
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testIgnoreDiffsNextActionLast() throws Exception {
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForPostedSwingRunnables();
|
||||
|
||||
AddressSet addrSet = new AddressSet(addr("1005e4f"), addr("1005e53"));
|
||||
setDiffSelection(addrSet);
|
||||
setLocation("1005e4f");
|
||||
ignoreAndNext();
|
||||
assertTrue(!ignoreDiffs.isEnabled());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.database.ProgramAddressFactory;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.TerminatedStringDataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.util.DiffUtility;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
public class DiffOverlayApplyTest extends DiffApplyTestAdapter {
|
||||
|
||||
public DiffOverlayApplyTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowHideDiffApplySettings() throws Exception {
|
||||
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
|
||||
builder.createMemory(".data", "0x2001000", 1000);
|
||||
|
||||
Program p1 = builder.getProgram();
|
||||
assertTrue(p1.getAddressFactory() instanceof ProgramAddressFactory);
|
||||
assertEquals(2, p1.getAddressFactory().getNumAddressSpaces()); // ram, OTHER
|
||||
|
||||
int id1 = p1.startTransaction("");
|
||||
Memory memory1 = p1.getMemory();
|
||||
MemoryBlock dataBlock1 = memory1.getBlock(".data");
|
||||
MemoryBlock overlayBlock1 =
|
||||
memory1.createInitializedBlock("OVL1", dataBlock1.getStart(), 0x20L, (byte) 0,
|
||||
TaskMonitorAdapter.DUMMY_MONITOR, true);
|
||||
assertEquals(3, p1.getAddressFactory().getNumAddressSpaces()); // ram, OTHER, OVL1
|
||||
|
||||
AddressSet addressSet1 = new AddressSet(overlayBlock1.getStart(), overlayBlock1.getEnd());
|
||||
byte[] bytes1 =
|
||||
{ 'a', 'p', 'p', 'l', 'e', (byte) 0, 'o', 'r', 'a', 'n', 'g', 'e', (byte) 0 };
|
||||
memory1.setBytes(overlayBlock1.getStart(), bytes1);
|
||||
|
||||
Listing listing1 = p1.getListing();
|
||||
Address overlayAddress1 = overlayBlock1.getStart();
|
||||
listing1.createData(overlayAddress1, new TerminatedStringDataType());
|
||||
overlayAddress1 = overlayAddress1.add(6);
|
||||
listing1.createData(overlayAddress1, new TerminatedStringDataType());
|
||||
|
||||
p1.endTransaction(id1, true);
|
||||
|
||||
ClassicSampleX86ProgramBuilder builder2 = new ClassicSampleX86ProgramBuilder();
|
||||
builder2.createMemory(".data", "0x2001000", 1000);
|
||||
Program p2 = builder2.getProgram();
|
||||
assertTrue(p2.getAddressFactory() instanceof ProgramAddressFactory);
|
||||
assertEquals(2, p2.getAddressFactory().getNumAddressSpaces());
|
||||
|
||||
int id2 = p2.startTransaction("");
|
||||
Memory memory2 = p2.getMemory();
|
||||
MemoryBlock dataBlock2 = memory2.getBlock(".data");
|
||||
MemoryBlock overlayBlock2 =
|
||||
memory2.createInitializedBlock("OVL1", dataBlock2.getStart(), 0x20L, (byte) 0,
|
||||
TaskMonitorAdapter.DUMMY_MONITOR, true);
|
||||
assertEquals(3, p2.getAddressFactory().getNumAddressSpaces());
|
||||
|
||||
AddressSet addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet1, p2);
|
||||
byte[] bytes2 =
|
||||
{ 'd', 'o', 'b', 'e', 'r', 'm', 'a', 'n', (byte) 0, 'p', 'o', 'o', 'd', 'l', 'e',
|
||||
(byte) 0 };
|
||||
memory2.setBytes(overlayBlock2.getStart(), bytes2);
|
||||
|
||||
Listing listing2 = p2.getListing();
|
||||
Address overlayAddress2 = overlayBlock2.getStart();
|
||||
listing2.createData(overlayAddress2, new TerminatedStringDataType());
|
||||
overlayAddress2 = overlayAddress2.add(9);
|
||||
listing2.createData(overlayAddress2, new TerminatedStringDataType());
|
||||
|
||||
p2.endTransaction(id2, true);
|
||||
|
||||
openProgram(p1);
|
||||
|
||||
openDiff(p2);
|
||||
setDiffSelection(addressSet2);
|
||||
apply();
|
||||
|
||||
Listing listing = p1.getListing();
|
||||
MemoryBlock overlayBlock = p1.getMemory().getBlock("OVL1");
|
||||
Address overlayAddress = overlayBlock.getStart();
|
||||
Data dataAt = listing.getDataAt(overlayAddress);
|
||||
assertNotNull(dataAt);
|
||||
assertEquals("doberman", dataAt.getValue());
|
||||
|
||||
overlayAddress = overlayBlock.getStart().add(9);
|
||||
dataAt = listing.getDataAt(overlayAddress);
|
||||
assertNotNull(dataAt);
|
||||
assertEquals("poodle", dataAt.getValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.awt.Window;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||
import ghidra.framework.main.FrontEndPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.project.tool.GhidraTool;
|
||||
import ghidra.framework.project.tool.ToolIconURL;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.test.TestEnv;
|
||||
|
||||
public class DiffSaveSettingsTest extends DiffApplyTestAdapter {
|
||||
|
||||
public DiffSaveSettingsTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
fixupGUI();
|
||||
env = new TestEnv();
|
||||
frontEndTool = env.showFrontEndTool();
|
||||
frontEndPlugin = getPlugin(frontEndTool, FrontEndPlugin.class);
|
||||
}
|
||||
|
||||
private void launchTool() throws Exception {
|
||||
// Launch our own tool for the Diff so that we can close it and handle "Save Tool?".
|
||||
runSwing(() -> tool =
|
||||
(PluginTool) frontEndTool.getProject().getToolServices().launchTool("MyDiffTestTool",
|
||||
null));
|
||||
|
||||
cb = getPlugin(tool, CodeBrowserPlugin.class);
|
||||
diffPlugin = getPlugin(tool, ProgramDiffPlugin.class);
|
||||
diffListingPanel = diffPlugin.getListingPanel();
|
||||
fp1 = cb.getFieldPanel();
|
||||
fp2 = diffListingPanel.getFieldPanel();
|
||||
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
|
||||
}
|
||||
|
||||
private void showNewTool() throws Exception {
|
||||
// Create our own tool for the Diff so that we can close it and handle "Save Tool?".
|
||||
runSwing(() -> {
|
||||
tool = new GhidraTool(frontEndTool.getProject(), "MyDiffTestTool");
|
||||
tool.setIconURL(new ToolIconURL("preferences-system.png"));
|
||||
tool.setVisible(true);
|
||||
});
|
||||
|
||||
tool.addPlugin(ProgramManagerPlugin.class.getName());
|
||||
setUpCodeBrowserTool(tool);
|
||||
|
||||
diffListingPanel = diffPlugin.getListingPanel();
|
||||
fp1 = cb.getFieldPanel();
|
||||
fp2 = diffListingPanel.getFieldPanel();
|
||||
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() {
|
||||
Window win = getWindow("Select Other Program");
|
||||
if (win != null) {
|
||||
//This window should not be up, so cancel it.
|
||||
pressButton(win, "Cancel");
|
||||
}
|
||||
|
||||
closeOurTool();
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
void closeOurTool() {
|
||||
if (tool == null) {
|
||||
return;
|
||||
}
|
||||
DockingActionIf closeToolAction = getToolAction(tool, "Close Tool");
|
||||
if (closeToolAction == null) {
|
||||
return;
|
||||
}
|
||||
performAction(closeToolAction, false);
|
||||
try {
|
||||
tool.getToolFrame();
|
||||
}
|
||||
catch (RuntimeException e1) {
|
||||
tool = null;
|
||||
return; // The tool is closed.
|
||||
}
|
||||
|
||||
tool = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveDiffApplySettings() throws Exception {
|
||||
// String p3Name = "notepadSetup1ForDiffTest";
|
||||
// String p4Name = "notepadSetup2ForDiffTest";
|
||||
|
||||
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
|
||||
ProgramDB p3 = builder.getProgram();
|
||||
ProgramDB p4 = builder.getProgram();
|
||||
|
||||
showNewTool();
|
||||
openProgram(p3);
|
||||
openDiff(p4);
|
||||
showApplySettings();
|
||||
|
||||
isReplace(programContextApplyCB);
|
||||
isReplace(byteApplyCB);
|
||||
isReplace(codeUnitApplyCB);
|
||||
isReplace(refApplyCB);
|
||||
isMerge(plateCommentApplyCB);
|
||||
isMerge(preCommentApplyCB);
|
||||
isMerge(eolCommentApplyCB);
|
||||
isMerge(repeatableCommentApplyCB);
|
||||
isMerge(postCommentApplyCB);
|
||||
isMergeSetPrimary(labelApplyCB);
|
||||
isReplace(functionApplyCB);
|
||||
isReplace(bookmarkApplyCB);
|
||||
isReplace(propertiesApplyCB);
|
||||
|
||||
// Change the apply settings.
|
||||
ignore(programContextApplyCB);
|
||||
ignore(byteApplyCB);
|
||||
ignore(codeUnitApplyCB);
|
||||
ignore(refApplyCB);
|
||||
replace(plateCommentApplyCB);
|
||||
replace(preCommentApplyCB);
|
||||
replace(eolCommentApplyCB);
|
||||
replace(repeatableCommentApplyCB);
|
||||
replace(postCommentApplyCB);
|
||||
merge(labelApplyCB);
|
||||
ignore(functionApplyCB);
|
||||
ignore(bookmarkApplyCB);
|
||||
ignore(propertiesApplyCB);
|
||||
|
||||
// Save the settings.
|
||||
DockingActionIf saveApplySettingsAction =
|
||||
getAction(diffPlugin, "Save Default Diff Apply Settings");
|
||||
assertNotNull(saveApplySettingsAction);
|
||||
performAction(saveApplySettingsAction, true);
|
||||
|
||||
// Check the settings.
|
||||
isIgnore(programContextApplyCB);
|
||||
isIgnore(byteApplyCB);
|
||||
isIgnore(codeUnitApplyCB);
|
||||
isIgnore(refApplyCB);
|
||||
isReplace(plateCommentApplyCB);
|
||||
isReplace(preCommentApplyCB);
|
||||
isReplace(eolCommentApplyCB);
|
||||
isReplace(repeatableCommentApplyCB);
|
||||
isReplace(postCommentApplyCB);
|
||||
isMerge(labelApplyCB);
|
||||
isIgnore(functionApplyCB);
|
||||
isIgnore(bookmarkApplyCB);
|
||||
isIgnore(propertiesApplyCB);
|
||||
|
||||
ProgramManagerPlugin pm = getPlugin(tool, ProgramManagerPlugin.class);
|
||||
DockingActionIf closeAllProgramAction = getAction(pm, "Close All");
|
||||
assertNotNull(closeAllProgramAction);
|
||||
performAction(closeAllProgramAction, true);
|
||||
|
||||
openProgram(p3);
|
||||
|
||||
openDiff(p4);
|
||||
showApplySettings();
|
||||
|
||||
// Check the settings.
|
||||
isIgnore(programContextApplyCB);
|
||||
isIgnore(byteApplyCB);
|
||||
isIgnore(codeUnitApplyCB);
|
||||
isIgnore(refApplyCB);
|
||||
isReplace(plateCommentApplyCB);
|
||||
isReplace(preCommentApplyCB);
|
||||
isReplace(eolCommentApplyCB);
|
||||
isReplace(repeatableCommentApplyCB);
|
||||
isReplace(postCommentApplyCB);
|
||||
isMerge(labelApplyCB);
|
||||
isIgnore(functionApplyCB);
|
||||
isIgnore(bookmarkApplyCB);
|
||||
isIgnore(propertiesApplyCB);
|
||||
|
||||
DockingActionIf closeToolAction = getToolAction(tool, "Close Tool");
|
||||
performAction(closeToolAction, false);
|
||||
// Save Tool? (Save)
|
||||
Window dialog = waitForWindow("Save Tool?");
|
||||
assertNotNull("Couldn't find 'Save Tool?' dialog.", dialog);
|
||||
pressButtonByText(dialog, "Save");
|
||||
|
||||
launchTool();
|
||||
|
||||
// Open another Diff.
|
||||
openProgram(p3);
|
||||
openDiff(p4);
|
||||
showApplySettings();
|
||||
|
||||
// Check the settings.
|
||||
isIgnore(programContextApplyCB);
|
||||
isIgnore(byteApplyCB);
|
||||
isIgnore(codeUnitApplyCB);
|
||||
isIgnore(refApplyCB);
|
||||
isReplace(plateCommentApplyCB);
|
||||
isReplace(preCommentApplyCB);
|
||||
isReplace(eolCommentApplyCB);
|
||||
isReplace(repeatableCommentApplyCB);
|
||||
isReplace(postCommentApplyCB);
|
||||
isMerge(labelApplyCB);
|
||||
isIgnore(functionApplyCB);
|
||||
isIgnore(bookmarkApplyCB);
|
||||
isIgnore(propertiesApplyCB);
|
||||
|
||||
closeOurTool();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,959 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Window;
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.progmgr.MultiTabPanel;
|
||||
import ghidra.app.plugin.core.progmgr.MultiTabPlugin;
|
||||
import ghidra.app.util.viewer.field.OpenCloseField;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingModel;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.WordDataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DiffTest extends DiffTestAdapter {
|
||||
|
||||
@Test
|
||||
public void testViewDiffsEnablement() throws Exception {
|
||||
getDiffActions();
|
||||
assertNotNull(openClosePgm2);
|
||||
assertTrue(!openClosePgm2.isEnabled());
|
||||
loadProgram(diffTestP1);
|
||||
assertTrue(openClosePgm2.isEnabled());
|
||||
closeProgram();
|
||||
assertTrue(!openClosePgm2.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenDiffProgram() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
|
||||
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(!applyDiffs.isEnabled());
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
assertTrue(!ignoreDiffs.isEnabled());
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
assertTrue(!prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(!setPgm2Selection.isEnabled());
|
||||
|
||||
// Check background color where there is and isn't a difference.
|
||||
setLocation("1009942");
|
||||
FieldLocation cursorPosition = fp2.getCursorLocation();
|
||||
BigInteger index = cursorPosition.getIndex();
|
||||
Color bg = getBgColor(fp1, index);
|
||||
Color bg2 = getBgColor(fp2, index);
|
||||
assertEquals(bg, bg2);
|
||||
|
||||
setLocation("100a002");
|
||||
cursorPosition = fp2.getCursorLocation();
|
||||
index = cursorPosition.getIndex();
|
||||
bg = getBgColor(fp1, index);
|
||||
bg2 = getBgColor(fp2, index);
|
||||
assertEquals(bg, bg2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseDiffProgram() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
|
||||
closeDiff();
|
||||
assertNull(diffListingPanel.getProgram());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelDiff() throws Exception {
|
||||
|
||||
// this latch lets us keep the diff from progressing until we are ready
|
||||
CountDownLatch latch = installDiffBlockingLatch();
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
pickSecondProgram(diffTestP2);
|
||||
waitForTasks();
|
||||
|
||||
Window win = waitForWindow("Determine Program Differences");
|
||||
pressButton(win, "OK");
|
||||
|
||||
win = waitForWindow("Checking Program Differences");
|
||||
pressButton(win, "Cancel");
|
||||
|
||||
win = waitForWindow("Cancel?");
|
||||
pressButton(win, "Yes");
|
||||
|
||||
// now that we have pressed cancel, it is safe to let the diff continue
|
||||
latch.countDown();
|
||||
|
||||
waitForCondition(() -> getWindow("Checking Program Differences") == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testViewDiffsAction() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
pickSecondProgram(diffTestP2);
|
||||
|
||||
waitForTasks();
|
||||
|
||||
Window win = waitForWindow("Determine Program Differences");
|
||||
|
||||
pressButton(win, "Cancel");
|
||||
getDiffActions();
|
||||
|
||||
invokeLater(viewDiffs);
|
||||
Window window = waitForWindow("Diff Already In Progress");
|
||||
assertNotNull(window);
|
||||
pressButtonByText(window, "OK");
|
||||
|
||||
invokeLater(getDiffs);
|
||||
win = waitForWindow("Determine Program Differences");
|
||||
pressButton(win, "OK");
|
||||
waitForSwing();
|
||||
|
||||
waitForCondition(() -> getWindow("Checking Program Differences") == null);
|
||||
waitForTasks(); // this waits for the task and swing thread to finish posting results
|
||||
|
||||
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
|
||||
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNextDiffAction() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
invokeLater(nextDiff);
|
||||
|
||||
assertEquals(addr("1001034"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1001034"), addr("1001034")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNextDiffAction2() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
invokeLater(nextDiff);
|
||||
|
||||
assertEquals(addr("1005e4f"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreviousDiffAction() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("100f3ff")), program));
|
||||
assertEquals(addr("100f3ff"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
invokeLater(prevDiff);
|
||||
|
||||
assertEquals(addr("1005e4f"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreviousDiffAction2() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
invokeLater(prevDiff);
|
||||
|
||||
assertEquals(addr("100415a"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("100415a"), addr("100415a")));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1002055")), program));
|
||||
assertEquals(addr("1002055"), getDiffAddress());
|
||||
|
||||
invokeLater(prevDiff);
|
||||
|
||||
assertEquals(addr("100204c"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("100204c"), addr("100204c")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowHideDiffDetails() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(diffDetails);
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
assertEquals(false, isDiffDetailsDisplayed());
|
||||
|
||||
invokeLater(diffDetails); // show
|
||||
waitForSwing();
|
||||
assertEquals(true, isDiffDetailsDisplayed());
|
||||
|
||||
runSwing(() -> {
|
||||
DiffDetailsProvider detailsProvider = diffPlugin.getDiffDetailsProvider();
|
||||
detailsProvider.closeComponent(); // hide
|
||||
}, false);
|
||||
waitForSwing();
|
||||
assertEquals(false, isDiffDetailsDisplayed());
|
||||
}
|
||||
|
||||
boolean isDiffDetailsDisplayed() {
|
||||
boolean shown = isProviderShown(tool.getToolFrame(), "Diff Details");
|
||||
if (!shown) {
|
||||
return false;
|
||||
}
|
||||
JPanel detailsPanel = (JPanel) findComponentByName(tool.getToolFrame(),
|
||||
DiffDetailsProvider.DIFF_DETAILS_PANEL);
|
||||
return detailsPanel != null;
|
||||
}
|
||||
|
||||
void checkDetails() {
|
||||
JPanel detailsPanel =
|
||||
(JPanel) findComponentByName(tool.getToolFrame(), "Diff Details Panel");
|
||||
assertNotNull(detailsPanel);
|
||||
JTextArea textArea = (JTextArea) findComponentByName(detailsPanel,
|
||||
DiffDetailsProvider.DIFF_DETAILS_TEXT_AREA);
|
||||
assertNotNull(textArea);
|
||||
JCheckBox autoUpdateCB = (JCheckBox) findComponentByName(detailsPanel,
|
||||
DiffDetailsProvider.AUTO_UPDATE_CHECK_BOX);
|
||||
assertNotNull(autoUpdateCB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffDetailsAction() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(diffDetails);
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("100")), program));
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
invokeLater(diffDetails);
|
||||
|
||||
// Check where there are differences
|
||||
assertEquals(true, isDiffDetailsDisplayed());
|
||||
JPanel detailsPanel = (JPanel) findComponentByName(tool.getToolFrame(),
|
||||
DiffDetailsProvider.DIFF_DETAILS_PANEL);
|
||||
assertNotNull(detailsPanel);
|
||||
JEditorPane textArea = (JEditorPane) findComponentByName(detailsPanel,
|
||||
DiffDetailsProvider.DIFF_DETAILS_TEXT_AREA);
|
||||
assertNotNull(textArea);
|
||||
String info = textArea.getText();
|
||||
assertTrue(info.indexOf("Byte Diffs") != -1);
|
||||
assertTrue(info.indexOf("Bookmark Diffs") == -1);
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1001014")), program));
|
||||
assertEquals(addr("1001014"), getDiffAddress());
|
||||
invokeLater(diffDetails);
|
||||
waitForSwing();
|
||||
assertEquals(true, isDiffDetailsDisplayed());
|
||||
|
||||
// Check where there are no differences
|
||||
info = textArea.getText();
|
||||
assertTrue(info.indexOf("No differences") != -1);
|
||||
|
||||
assertEquals(addr("1001014"), getDiffAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectAllDiffsAction() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(diffDetails);
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
|
||||
invokeLater(selectAllDiffs);
|
||||
|
||||
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
|
||||
checkIfSameSelection(expectedSelection, cb.getCurrentSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPgm2SelectionAction() {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
assertNotNull(setPgm2Selection);
|
||||
assertTrue(!setPgm2Selection.isEnabled());
|
||||
|
||||
AddressSet as = new AddressSet();
|
||||
as.addRange(addr("1001008"), addr("100103f"));
|
||||
as.addRange(addr("1001965"), addr("1001968"));
|
||||
as.addRange(addr("1002053"), addr("1002054"));
|
||||
as.addRange(addr("10022e9"), addr("1002309"));
|
||||
as.addRange(addr("100a000"), addr("100a006"));
|
||||
|
||||
AddressSet diffAs = new AddressSet();
|
||||
diffAs.addRange(addr("1001034"), addr("1001034"));
|
||||
diffAs.addRange(addr("10022ee"), addr("10022fc"));
|
||||
diffAs.addRange(addr("1002304"), addr("1002304"));
|
||||
diffAs.addRange(addr("1002306"), addr("1002306"));
|
||||
|
||||
tool.firePluginEvent(
|
||||
new ProgramSelectionPluginEvent("test", new ProgramSelection(as), program));
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1001000")), program));
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
|
||||
invokeLater(setPgm2Selection);
|
||||
|
||||
assertEquals(new ProgramSelection(diffAs), cb.getCurrentSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeToEntireViewWithDiff() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JTree tree = getProgramTree();
|
||||
|
||||
// Replace view with .data
|
||||
selectTreeNodeByText(tree, ".data");
|
||||
invokeAndWait(replaceView);
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("10085ff"), cb.getCurrentAddress());
|
||||
|
||||
// Replace with program view
|
||||
selectTreeNodeByText(tree, "DiffTestPgm1");
|
||||
invokeAndWait(replaceView);
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("100"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
|
||||
|
||||
assertEquals(new ProgramSelection(getSetupAllDiffsSet()),
|
||||
diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplaySingleFragmentWithDiff() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JTree tree = getProgramTree();
|
||||
selectTreeNodeByText(tree, ".data");
|
||||
|
||||
runSwing(() -> replaceView.actionPerformed(new ActionContext()));
|
||||
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("10085ff"), cb.getCurrentAddress());
|
||||
|
||||
assertEquals(new ProgramSelection(getSetupAllDiffsSet()),
|
||||
diffPlugin.getDiffHighlightSelection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplayMultipleFragmentsWithDiff() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
programBuilderDiffTest2.createComment("0x01008000", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x01008607", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x01008a99", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x0100a001", "My comment", CodeUnit.EOL_COMMENT);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JTree tree = getProgramTree();
|
||||
selectTreeNodeByText(tree, ".data");
|
||||
|
||||
runSwing(() -> replaceView.actionPerformed(new ActionContext()));
|
||||
|
||||
selectTreeNodeByText(tree, ".rsrc");
|
||||
|
||||
runSwing(() -> goToView.actionPerformed(new ActionContext()));
|
||||
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
|
||||
|
||||
AddressSet diffSet = new AddressSet(getSetupAllDiffsSet());
|
||||
diffSet.add(addr("0x01008000"));
|
||||
diffSet.add(addr("0x01008607"));
|
||||
diffSet.add(addr("0x01008a99"));
|
||||
diffSet.add(addr("0x0100a001"));
|
||||
assertEquals(new ProgramSelection(diffSet), diffPlugin.getDiffHighlightSelection());
|
||||
|
||||
AddressSet viewSet = new AddressSet();
|
||||
viewSet.addRange(addr("1008000"), addr("10085ff")); // .data
|
||||
viewSet.addRange(addr("100a000"), addr("100f3ff")); // .rsrc
|
||||
assertEquals(viewSet, cb.getView());
|
||||
|
||||
// top of View .rsrc
|
||||
cb.goTo(new ProgramLocation(program, addr("100a000")));
|
||||
assertEquals(addr("100a000"), cb.getCurrentAddress());
|
||||
assertEquals(addr("100a000"), getDiffAddress());
|
||||
|
||||
// Previous Diff should add .datau to the view
|
||||
invokeLater(prevDiff);
|
||||
waitForSwing();
|
||||
viewSet.addRange(addr("1008600"), addr("1009943")); // .datau
|
||||
assertEquals(addr("1008a99"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1008a99"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1008a99"), addr("1008a99")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
|
||||
// top of View .data
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1008000"), getDiffAddress());
|
||||
|
||||
// Previous Diff should add .text to the view
|
||||
invokeLater(prevDiff);
|
||||
|
||||
viewSet.addRange(addr("1001000"), addr("10075ff"));
|
||||
assertEquals(addr("1005e4f"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1005e4f"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyViewDiff() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
programBuilderDiffTest2.createComment("0x01008000", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x01008607", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x01009943", "My comment", CodeUnit.EOL_COMMENT);
|
||||
programBuilderDiffTest2.createComment("0x0100a001", "My comment", CodeUnit.EOL_COMMENT);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
JTree tree = getProgramTree();
|
||||
selectTreeNodeByText(tree, "DiffTestPgm1");
|
||||
performAction(removeView, true);
|
||||
AddressSet viewSet = new AddressSet();
|
||||
assertEquals(viewSet, cb.getView());
|
||||
topOfFile(fp1);
|
||||
assertNull(cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertNull(cb.getCurrentAddress());
|
||||
fp1.getHeight();
|
||||
assertEquals(0, fp1.getLayoutModel().getNumIndexes().intValue());
|
||||
assertEquals(0, fp2.getLayoutModel().getNumIndexes().intValue());
|
||||
|
||||
AddressSet diffSet = new AddressSet(getSetupAllDiffsSet());
|
||||
diffSet.add(addr("0x01008000"));
|
||||
diffSet.add(addr("0x01008607"));
|
||||
diffSet.add(addr("0x01009943"));
|
||||
diffSet.add(addr("0x0100a001"));
|
||||
assertEquals(new ProgramSelection(diffSet), diffPlugin.getDiffHighlightSelection());
|
||||
assertEquals(addr("100"), diffPlugin.getCurrentAddress());
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
|
||||
// Next Diff should add .text to the view
|
||||
invokeLater(nextDiff);
|
||||
|
||||
viewSet.addRange(addr("1001000"), addr("10075ff"));
|
||||
assertEquals(addr("1001034"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1001034"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1001034"), addr("1001034")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
|
||||
// bottom of View .text
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("10075ff"), cb.getCurrentAddress());
|
||||
assertEquals(addr("10075ff"), getDiffAddress());
|
||||
|
||||
// Next Diff should add .data to the view
|
||||
invokeLater(nextDiff);
|
||||
waitForSwing();
|
||||
viewSet.addRange(addr("1008000"), addr("10085ff"));
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1008000"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1008000"), addr("1008000")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
|
||||
// bottom of View .data
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("10085ff"), cb.getCurrentAddress());
|
||||
assertEquals(addr("10085ff"), getDiffAddress());
|
||||
|
||||
// Next Diff should add .datau to the view
|
||||
invokeLater(nextDiff);
|
||||
|
||||
viewSet.addRange(addr("1008600"), addr("1009943"));
|
||||
assertEquals(addr("1008607"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1008607"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("1008607"), addr("1008607")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
|
||||
// bottom of View .datau
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("1009943"), cb.getCurrentAddress());
|
||||
assertEquals(addr("1009943"), getDiffAddress());
|
||||
|
||||
// Next Diff should add .rsrc to the view
|
||||
invokeLater(nextDiff);
|
||||
|
||||
viewSet.addRange(addr("100a000"), addr("100f3ff"));
|
||||
assertEquals(addr("100a001"), cb.getCurrentAddress());
|
||||
assertEquals(addr("100a001"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("100a001"), addr("100a001")));
|
||||
assertEquals(viewSet, cb.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffWithChangeTabs() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
ProgramBuilder builder3 = new ProgramBuilder("Program3", ProgramBuilder._X86);
|
||||
builder3.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder3.createMemory(".data", "0x1008000", 0x600);
|
||||
ProgramDB program3 = builder3.getProgram();
|
||||
|
||||
ProgramBuilder builder4 = new ProgramBuilder("Program4", ProgramBuilder._X86);
|
||||
builder4.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder4.createMemory(".data", "0x1008000", 0x600);
|
||||
ProgramDB program4 = builder4.getProgram();
|
||||
|
||||
tool.removePlugins(new Plugin[] { pt });
|
||||
tool.addPlugin(MultiTabPlugin.class.getName());
|
||||
openProgram(program3);
|
||||
openProgram(program4);
|
||||
openProgram(diffTestP1);
|
||||
|
||||
openDiff(diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
|
||||
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
|
||||
|
||||
MultiTabPanel panel = findComponent(tool.getToolFrame(), MultiTabPanel.class);
|
||||
|
||||
assertEquals(true, isDiffing());
|
||||
assertEquals(true, isShowingDiff());
|
||||
// Check action enablement.
|
||||
checkDiffAction("View Program Differences", true, true);
|
||||
checkDiffAction("Open/Close Program View", true, true);
|
||||
checkDiffAction("Apply Differences", true, false);
|
||||
checkDiffAction("Apply Differences and Goto Next Difference", true, false);
|
||||
checkDiffAction("Ignore Selection and Goto Next Difference", true, false);
|
||||
checkDiffAction("Next Difference", true, true);
|
||||
checkDiffAction("Previous Difference", true, false);
|
||||
checkDiffAction("Diff Location Details", true, true);
|
||||
checkDiffAction("Show Diff Apply Settings", true, true);
|
||||
checkDiffAction("Get Differences", true, true);
|
||||
checkDiffAction("Select All Differences", true, true);
|
||||
checkDiffAction("Set Program1 Selection On Program2", true, false);
|
||||
|
||||
// Check background color where there is and isn't a difference.
|
||||
setLocation("1002995");
|
||||
assertEquals(addr("1002995"), diffPlugin.getCurrentAddress());
|
||||
BigInteger index = fp2.getCursorLocation().getIndex();
|
||||
Color bg = getBgColor(fp1, index);
|
||||
Color bg2 = getBgColor(fp2, index);
|
||||
assertEquals(bg, bg2);
|
||||
checkDiffAction("Next Difference", true, true);
|
||||
checkDiffAction("Previous Difference", true, true);
|
||||
setLocation("100299e");
|
||||
index = fp2.getCursorLocation().getIndex();
|
||||
assertEquals(addr("100299e"), diffPlugin.getCurrentAddress());
|
||||
bg = getBgColor(fp1, index);
|
||||
bg2 = getBgColor(fp2, index);
|
||||
assertEquals(bg, bg2);
|
||||
checkDiffAction("Next Difference", true, true);
|
||||
checkDiffAction("Previous Difference", true, true);
|
||||
|
||||
selectTab(panel, program3);
|
||||
assertEquals(true, isDiffing());
|
||||
assertEquals(false, isShowingDiff());
|
||||
// Check action enablement.
|
||||
checkDiffAction("View Program Differences", true, true);
|
||||
checkDiffAction("Apply Differences", false, false);
|
||||
checkDiffAction("Apply Differences and Goto Next Difference", false, false);
|
||||
checkDiffAction("Ignore Selection and Goto Next Difference", false, false);
|
||||
checkDiffAction("Next Difference", false, false);
|
||||
checkDiffAction("Previous Difference", false, false);
|
||||
checkDiffAction("Diff Location Details", false, false);
|
||||
checkDiffAction("Show Diff Apply Settings", false, false);
|
||||
checkDiffAction("Get Differences", false, false);
|
||||
checkDiffAction("Select All Differences", false, false);
|
||||
checkDiffAction("Set Program1 Selection On Program2", false, false);
|
||||
|
||||
selectTab(panel, program4);
|
||||
assertEquals(true, isDiffing());
|
||||
assertEquals(false, isShowingDiff());
|
||||
// Check action enablement.
|
||||
checkDiffAction("View Program Differences", true, true);
|
||||
checkDiffAction("Apply Differences", false, false);
|
||||
checkDiffAction("Apply Differences and Goto Next Difference", false, false);
|
||||
checkDiffAction("Ignore Selection and Goto Next Difference", false, false);
|
||||
checkDiffAction("Next Difference", false, false);
|
||||
checkDiffAction("Previous Difference", false, false);
|
||||
checkDiffAction("Diff Location Details", false, false);
|
||||
checkDiffAction("Show Diff Apply Settings", false, false);
|
||||
checkDiffAction("Get Differences", false, false);
|
||||
checkDiffAction("Select All Differences", false, false);
|
||||
checkDiffAction("Set Program1 Selection On Program2", false, false);
|
||||
|
||||
selectTab(panel, diffTestP1);
|
||||
assertEquals(true, isDiffing());
|
||||
assertEquals(true, isShowingDiff());
|
||||
assertEquals(addr("100299e"), diffPlugin.getCurrentAddress());
|
||||
// Check action enablement.
|
||||
checkDiffAction("View Program Differences", true, true);
|
||||
checkDiffAction("Apply Differences", true, false);
|
||||
checkDiffAction("Apply Differences and Goto Next Difference", true, false);
|
||||
checkDiffAction("Ignore Selection and Goto Next Difference", true, false);
|
||||
checkDiffAction("Next Difference", true, true);
|
||||
checkDiffAction("Previous Difference", true, true);
|
||||
checkDiffAction("Diff Location Details", true, true);
|
||||
checkDiffAction("Show Diff Apply Settings", true, true);
|
||||
checkDiffAction("Get Differences", true, true);
|
||||
checkDiffAction("Select All Differences", true, true);
|
||||
checkDiffAction("Set Program1 Selection On Program2", true, false);
|
||||
|
||||
// Now close the Diff.
|
||||
closeDiff();
|
||||
assertNull(diffListingPanel.getProgram());
|
||||
assertEquals(false, isDiffing());
|
||||
assertEquals(false, isShowingDiff());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenCloseProgramAction() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
ProgramBuilder builder3 = new ProgramBuilder("Program3", ProgramBuilder._X86);
|
||||
builder3.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder3.createMemory(".data", "0x1008000", 0x600);
|
||||
ProgramDB program3 = builder3.getProgram();
|
||||
|
||||
ProgramBuilder builder4 = new ProgramBuilder("Program4", ProgramBuilder._X86);
|
||||
builder4.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder4.createMemory(".data", "0x1008000", 0x600);
|
||||
ProgramDB program4 = builder4.getProgram();
|
||||
|
||||
tool.removePlugins(new Plugin[] { pt });
|
||||
tool.addPlugin(MultiTabPlugin.class.getName());
|
||||
openProgram(program3);
|
||||
openProgram(program4);
|
||||
openProgram(diffTestP1);
|
||||
|
||||
openDiff(diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
|
||||
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
|
||||
|
||||
MultiTabPanel panel = findComponent(tool.getToolFrame(), MultiTabPanel.class);
|
||||
|
||||
assertEquals(true, isDiffing());
|
||||
assertEquals(true, isShowingDiff());
|
||||
checkDiffAction("Open/Close Program View", true, true);
|
||||
|
||||
//
|
||||
// Different tab--still enabled
|
||||
//
|
||||
selectTab(panel, program3);
|
||||
checkDiffAction("Open/Close Program View", true, true);
|
||||
|
||||
clickDiffButton();
|
||||
assertTrue("Not diffing after clicking the diff button when on a non-diff tab",
|
||||
isDiffing());
|
||||
assertTrue("Diff not showing after clicking the diff button when on a non-diff tab",
|
||||
isShowingDiff());
|
||||
|
||||
//
|
||||
// Diff tab--still enabled
|
||||
//
|
||||
checkDiffAction("Open/Close Program View", true, true);
|
||||
|
||||
clickDiffButton();
|
||||
|
||||
DialogComponentProvider dialogProvider = waitForDialogComponent("Close Diff Session");
|
||||
assertNotNull("Did not get confirmation dialog", dialogProvider);
|
||||
pressButtonByText(dialogProvider.getComponent(), "Yes", true);
|
||||
waitForSwing();
|
||||
|
||||
assertFalse("Still diffing after clicking the diff button when on the diff tab",
|
||||
isDiffing());
|
||||
assertFalse("Still showing diff after clicking the diff button when on the diff tab",
|
||||
isShowingDiff());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenCloseArrayWithDiffShowing() throws Exception {
|
||||
|
||||
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
|
||||
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
|
||||
|
||||
openDiff(diffTestP1, diffTestP2);
|
||||
waitForSwing();
|
||||
|
||||
ArrayDataType array = new ArrayDataType(new WordDataType(), 12, 2);
|
||||
CreateDataCmd cmd = new CreateDataCmd(addr("0x00000106"), array);
|
||||
tool.execute(cmd, diffTestP1);
|
||||
|
||||
Data data = diffTestP1.getListing().getDataAt(addr("0x00000106"));
|
||||
ListingModel listingModel = cb.getListingModel();
|
||||
cb.goToField(addr("0x00000106"), "+", 0, 0);
|
||||
assertTrue(cb.getCurrentField() instanceof OpenCloseField);
|
||||
assertFalse("Array is not closed as expected.", listingModel.isOpen(data));
|
||||
cb.goToField(addr("0x00000120"), "Address", 0, 0);
|
||||
assertEquals("00000120", cb.getCurrentFieldText());
|
||||
cb.goToField(addr("0x00000106"), "Address", 0, 0);
|
||||
assertEquals("00000106", cb.getCurrentFieldText());
|
||||
|
||||
cb.goToField(addr("0x00000106"), "+", 0, 0);
|
||||
click(cb, 1);
|
||||
waitForSwing();
|
||||
assertTrue("Array failed to open.", listingModel.isOpen(data));
|
||||
|
||||
cb.goToField(addr("0x00000106"), "+", 0, 0);
|
||||
click(cb, 1);
|
||||
waitForSwing();
|
||||
cb.goToField(addr("0x00000120"), "Address", 0, 0);
|
||||
cb.goToField(addr("0x00000106"), "Address", 0, 0);
|
||||
assertEquals("00000106", cb.getCurrentFieldText());
|
||||
assertFalse("Array failed to close.", listingModel.isOpen(data));
|
||||
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private Color getBgColor(FieldPanel fp, BigInteger index) {
|
||||
return runSwing(() -> fp.getBackgroundColor(index));
|
||||
}
|
||||
|
||||
private CountDownLatch installDiffBlockingLatch() {
|
||||
final CountDownLatch continueLatch = new CountDownLatch(1);
|
||||
|
||||
diffPlugin.setDiffTaskListener(inProgress -> {
|
||||
try {
|
||||
continueLatch.await(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// shouldn't happen
|
||||
throw new RuntimeException("Unexpectedly interrupted while blocking the diff task!",
|
||||
e);
|
||||
}
|
||||
});
|
||||
return continueLatch;
|
||||
}
|
||||
|
||||
private void clickDiffButton() {
|
||||
runSwing(() -> {
|
||||
openClosePgm2.setSelected(!openClosePgm2.isSelected());
|
||||
openClosePgm2.actionPerformed(new ActionContext());
|
||||
}, false);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified Diff action is in the indicated state and
|
||||
* causes a JUnit assert if not.
|
||||
*
|
||||
* @param actionName the name of the DIff action.
|
||||
* @param inTool the action is currently added to the tool.
|
||||
* @param enabled the action is enabled.
|
||||
*/
|
||||
private void checkDiffAction(String actionName, boolean inTool, boolean enabled) {
|
||||
DockingActionIf tmpAction = getAction(diffPlugin, actionName);
|
||||
if (inTool) {
|
||||
if (tmpAction == null) {
|
||||
assertNotNull("Diff action, " + actionName + ", was not in the tool as expected.",
|
||||
tmpAction);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tmpAction != null) {
|
||||
assertNull("Diff action, " + actionName + ", was unexpectedly found in the tool.",
|
||||
tmpAction);
|
||||
}
|
||||
}
|
||||
if (tmpAction == null) {
|
||||
return;
|
||||
}
|
||||
assertEquals("Diff action, " + actionName + ", was unexpectedly " +
|
||||
(enabled ? "disabled" : "enabled") + ".", enabled, tmpAction.isEnabled());
|
||||
}
|
||||
|
||||
private boolean isDiffing() {
|
||||
if (diffPlugin == null) {
|
||||
return false;
|
||||
}
|
||||
if (diffPlugin.getFirstProgram() == null || diffPlugin.getDiffController() == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isShowingDiff() {
|
||||
if (diffPlugin == null) {
|
||||
return false;
|
||||
}
|
||||
Program currentProgram = diffPlugin.getCurrentProgram();
|
||||
Program firstProgram = diffPlugin.getFirstProgram();
|
||||
if (currentProgram == null || currentProgram != firstProgram) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void selectTab(final MultiTabPanel panel, final Program pgm) {
|
||||
runSwing(() -> invokeInstanceMethod("setSelectedProgram", panel,
|
||||
new Class[] { Program.class }, new Object[] { pgm }), true);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void selectTreeNodeByText(final JTree tree, final String text) throws Exception {
|
||||
runSwing(() -> {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.expandPath(path);
|
||||
});
|
||||
|
||||
waitForSwing();
|
||||
|
||||
runSwing(() -> {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.getSelectionModel().setSelectionPath(path);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.MultiLineLabel;
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
import ghidra.app.cmd.label.AddLabelCmd;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
|
||||
public class DualProgramTest extends DiffTestAdapter {
|
||||
|
||||
@Test
|
||||
public void testOpenClosePgm2Enablement() throws Exception {
|
||||
assertNotNull(openClosePgm2);
|
||||
assertTrue(!openClosePgm2.isEnabled());
|
||||
loadProgram(diffTestP1);
|
||||
assertTrue(openClosePgm2.isEnabled());
|
||||
closeProgram();
|
||||
assertTrue(!openClosePgm2.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelOpenSecondProgram() throws Exception {
|
||||
Window win;
|
||||
Component comp;
|
||||
|
||||
restoreProgram(diffTestP2);
|
||||
loadProgram(diffTestP1);
|
||||
launchDiffByAction();
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
|
||||
pressButton(win, "Cancel");
|
||||
waitForSwing();
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
|
||||
win = getWindow("Select Other Program");
|
||||
assertNull(win);
|
||||
closeProgram();
|
||||
assertNull(cb.getCurrentLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeOpenSecondProgram() throws Exception {
|
||||
|
||||
restoreProgram(diffTestP2);
|
||||
loadProgram(diffTestP1);
|
||||
launchDiffByAction();
|
||||
Window win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
waitForSwing();
|
||||
|
||||
Component comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
|
||||
triggerActionKey(comp, 0, KeyEvent.VK_ESCAPE);
|
||||
waitForSwing();
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
|
||||
win = getWindow("Select Other Program");
|
||||
assertNull(win);
|
||||
closeProgram();
|
||||
assertNull(cb.getCurrentLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenSecondProgram() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(!applyDiffs.isEnabled());
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
assertTrue(!ignoreDiffs.isEnabled());
|
||||
assertTrue(!nextDiff.isEnabled());
|
||||
assertTrue(!prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(!diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(!setPgm2Selection.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseSecondProgram() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
|
||||
closeDiff();
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonMatchingProgramType() throws Exception {
|
||||
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._SPARC64);
|
||||
ProgramDB otherProgram = otherBuilder.getProgram();
|
||||
|
||||
restoreProgram(otherProgram);
|
||||
loadProgram(diffTestP1);
|
||||
launchDiffByAction();
|
||||
Window win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
Component comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
|
||||
pressButton(win, "OK");
|
||||
|
||||
waitForTasks();
|
||||
win = waitForWindow("Can't Open Selected Program");
|
||||
assertNotNull(win);
|
||||
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
|
||||
assertTrue(mll.getLabel().startsWith("Programs languages don't match."));
|
||||
pressButton(win, "OK");
|
||||
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
pressButton(win, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMemInCommonContinueNo() throws Exception {
|
||||
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
|
||||
ProgramDB otherProgram = otherBuilder.getProgram();
|
||||
otherBuilder.createMemory(".stuff", "0x1000600", 0x300);
|
||||
|
||||
restoreProgram(otherProgram);
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
launchDiffByAction();
|
||||
Window win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
Component comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
|
||||
pressButton(win, "OK");
|
||||
waitForTasks();
|
||||
win = waitForWindow("No Memory In Common");
|
||||
assertNotNull(win);
|
||||
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
|
||||
assertTrue(
|
||||
mll.getLabel().startsWith("The two programs have no memory addresses in common."));
|
||||
pressButton(win, "No");
|
||||
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
pressButton(win, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMemInCommonContinueYes() throws Exception {
|
||||
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
|
||||
ProgramDB otherProgram = otherBuilder.getProgram();
|
||||
otherBuilder.createMemory(".stuff", "0x1000600", 0x300);
|
||||
|
||||
restoreProgram(otherProgram);
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
launchDiffByAction();
|
||||
Window win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
Component comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
|
||||
pressButton(win, "OK");
|
||||
waitForTasks();
|
||||
win = waitForWindow("No Memory In Common");
|
||||
assertNotNull(win);
|
||||
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
|
||||
assertTrue(
|
||||
mll.getLabel().startsWith("The two programs have no memory addresses in common."));
|
||||
pressButton(win, "Yes");
|
||||
|
||||
win = waitForWindow("Determine Program Differences");
|
||||
assertNotNull(win);
|
||||
pressButton(win, "OK");
|
||||
assertTrue(!win.isShowing());
|
||||
|
||||
waitForTasks();
|
||||
|
||||
win = waitForWindow("Memory Differs");
|
||||
assertNotNull(win);
|
||||
pressButton(win, "OK");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffPgmSameLanguage() throws Exception {
|
||||
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
|
||||
ProgramDB otherProgram = otherBuilder.getProgram();
|
||||
otherBuilder.createMemory(".stuff", "0x1004000", 0x300);
|
||||
|
||||
Window win;
|
||||
Component comp;
|
||||
//InfoWindow.showSplashScreen();
|
||||
showTool(frontEndTool);
|
||||
env.showTool();
|
||||
|
||||
restoreProgram(otherProgram);
|
||||
loadProgram(diffTestP1);
|
||||
|
||||
launchDiffByAction();
|
||||
waitForSwing();
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
|
||||
pressButton(win, "OK");
|
||||
waitForTasks();
|
||||
waitForSwing();
|
||||
win = getWindow("Select Other Program");
|
||||
assertNull(win);
|
||||
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
assertEquals("DiffTestPgm1", diffPlugin.getCurrentProgram().getName());
|
||||
assertEquals("OtherProgram", diffPlugin.getSecondProgram().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplacePgm2() throws Exception {
|
||||
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
|
||||
ProgramDB otherProgram = otherBuilder.getProgram();
|
||||
otherBuilder.createMemory(".stuff", "0x1004000", 0x300);
|
||||
|
||||
Window win;
|
||||
Component comp;
|
||||
|
||||
restoreProgram(otherProgram);
|
||||
restoreProgram(diffTestP2);
|
||||
loadProgram(diffTestP1);
|
||||
getDiffActions();
|
||||
assertNull(fp2.getTopLevelAncestor());
|
||||
|
||||
launchDiffByAction();
|
||||
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
comp = getComponentOfType(win, JComboBox.class);
|
||||
assertNotNull(comp);
|
||||
|
||||
JTree tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
|
||||
pressButton(win, "OK");
|
||||
waitForTasks();
|
||||
waitForSwing();
|
||||
|
||||
win = getWindow("Select Other Program");
|
||||
assertNull(win);
|
||||
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||
assertEquals("DiffTestPgm1", pm.getCurrentProgram().getName());
|
||||
assertEquals("OtherProgram", diffPlugin.getSecondProgram().getName());
|
||||
|
||||
DialogComponentProvider dialog = waitForDialogComponent(DialogComponentProvider.class);
|
||||
// press the Cancel button
|
||||
pressButtonByText(dialog, "Cancel");
|
||||
waitForSwing();
|
||||
|
||||
closeDiff();
|
||||
assertFalse(openClosePgm2.isSelected());
|
||||
|
||||
runSwing(() -> {
|
||||
openClosePgm2.setSelected(true);
|
||||
openClosePgm2.actionPerformed(new ActionContext());
|
||||
}, false);
|
||||
waitForSwing();
|
||||
|
||||
waitForCondition(() -> openClosePgm2.isSelected());
|
||||
assertEquals(true, openClosePgm2.isSelected());
|
||||
|
||||
win = waitForWindow("Select Other Program");
|
||||
assertNotNull(win);
|
||||
tree = findComponent(win, JTree.class);
|
||||
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
|
||||
pressButton(win, "OK");
|
||||
waitForTasks();
|
||||
assertTrue(!win.isVisible());
|
||||
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
assertEquals("DiffTestPgm1", diffPlugin.getCurrentProgram().getName());
|
||||
assertEquals("DiffTestPgm2", diffPlugin.getSecondProgram().getName());
|
||||
|
||||
dialog = waitForDialogComponent(DialogComponentProvider.class);
|
||||
// press the Cancel button
|
||||
pressButtonByText(dialog, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenSameSecondProgram() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP1);
|
||||
assertNotNull(fp2.getTopLevelAncestor());
|
||||
|
||||
// Check action enablement.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
assertTrue(!applyDiffs.isEnabled());
|
||||
assertTrue(!applyDiffsNext.isEnabled());
|
||||
assertTrue(!ignoreDiffs.isEnabled());
|
||||
assertTrue(!nextDiff.isEnabled());
|
||||
assertTrue(!prevDiff.isEnabled());
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
assertTrue(!diffApplySettings.isEnabled());
|
||||
assertTrue(getDiffs.isEnabled());
|
||||
assertTrue(selectAllDiffs.isEnabled());
|
||||
assertTrue(!setPgm2Selection.isEnabled());
|
||||
|
||||
// Modify the active program.
|
||||
setLocation("100f3ff");
|
||||
CompoundCmd cmd = new CompoundCmd("test");
|
||||
cmd.add(new AddLabelCmd(addr("100f3ff"), "TestLabel", false, SourceType.USER_DEFINED));
|
||||
cmd.add(
|
||||
new AddLabelCmd(addr("100f3ff"), "AnotherTestLabel", false, SourceType.USER_DEFINED));
|
||||
try {
|
||||
txId = diffTestP1.startTransaction("Modify Program");
|
||||
cmd.applyTo(diffTestP1);
|
||||
}
|
||||
finally {
|
||||
diffTestP1.endTransaction(txId, true);
|
||||
}
|
||||
|
||||
// Check that the two programs now differ.
|
||||
assertTrue(viewDiffs.isEnabled());
|
||||
LayoutModel lm1 = fp1.getLayoutModel();
|
||||
LayoutModel lm2 = fp2.getLayoutModel();
|
||||
int num1 = lm1.getNumIndexes().intValue();
|
||||
int num2 = lm2.getNumIndexes().intValue();
|
||||
assertEquals(num1, num2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntireViewDiff() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
|
||||
selectTreeNodeByText(tree, "DiffTestPgm1");
|
||||
performAction(replaceView, true);
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("00000100"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleFragmentDiff() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
|
||||
selectTreeNodeByText(tree, ".data");
|
||||
performAction(replaceView, true);
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("10085ff"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFragmentsDiff() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
|
||||
selectTreeNodeByText(tree, ".data");
|
||||
performAction(replaceView, true);
|
||||
selectTreeNodeByText(tree, ".rsrc");
|
||||
|
||||
performAction(goToView, true);
|
||||
|
||||
topOfFile(fp1);
|
||||
assertEquals(addr("1008000"), cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyViewDiff() throws Exception {
|
||||
openSecondProgram(diffTestP1, diffTestP2);
|
||||
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
|
||||
selectTreeNodeByText(tree, "DiffTestPgm1");
|
||||
performAction(removeView, true);
|
||||
topOfFile(fp1);
|
||||
assertNull(cb.getCurrentAddress());
|
||||
bottomOfFile(fp1);
|
||||
assertNull(cb.getCurrentAddress());
|
||||
assertEquals(0, fp1.getLayoutModel().getNumIndexes().intValue());
|
||||
assertEquals(0, fp2.getLayoutModel().getNumIndexes().intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a tree node in the indicated tree with the specified text.
|
||||
* The matching tree node is determined by comparing the specified text
|
||||
* with the string returned by the tree node's toString() method.
|
||||
* <br> Note: This method affects the expansion state of the tree. It
|
||||
* will expand nodes starting at the root until a match is found or all
|
||||
* of the tree is checked.
|
||||
* @param tree the tree
|
||||
* @param text the tree node's text
|
||||
*/
|
||||
public static void selectTreeNodeByText(final JTree tree, final String text) {
|
||||
runSwing(() -> {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.expandPath(path);
|
||||
}, false);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
runSwing(() -> {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.getSelectionModel().setSelectionPath(path);
|
||||
}, false);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.diff;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class TreeTestUtils {
|
||||
public static TreePath findTreePathToText(JTree tree, String text) {
|
||||
TreeModel tm = tree.getModel();
|
||||
GTreeNode rootNode = (GTreeNode) tm.getRoot();
|
||||
return findPathToText(tree, rootNode, text);
|
||||
}
|
||||
|
||||
protected static TreePath findPathToText(JTree tree, GTreeNode node, String text) {
|
||||
if (text.equals(node.getName())) {
|
||||
return node.getTreePath();
|
||||
}
|
||||
List<GTreeNode> allChildren = node.getAllChildren();
|
||||
for (GTreeNode childNode : allChildren) {
|
||||
TreePath treePath = findPathToText(tree, childNode, text);
|
||||
if (treePath != null) {
|
||||
return treePath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a tree node in the indicated tree with the specified text.
|
||||
* The matching tree node is determined by comparing the specified text
|
||||
* with the string returned by the tree node's toString() method.
|
||||
* <br> Note: This method affects the expansion state of the tree. It
|
||||
* will expand nodes starting at the root until a match is found or all
|
||||
* of the tree is checked.
|
||||
* @param tree the tree
|
||||
* @param text the tree node's text
|
||||
*/
|
||||
public static void selectTreeNodeByText(final JTree tree, final String text) {
|
||||
|
||||
AbstractGenericTest.runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.expandPath(path);
|
||||
}
|
||||
});
|
||||
|
||||
AbstractGenericTest.waitForSwing();
|
||||
|
||||
AbstractGenericTest.runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TreePath path = findTreePathToText(tree, text);
|
||||
if (path == null) {
|
||||
throw new RuntimeException("tree path is null.");
|
||||
}
|
||||
tree.getSelectionModel().setSelectionPath(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|