mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-4410 - Version Tracking - Added support for deleting matches; Added table column filters
This commit is contained in:
parent
76977bd514
commit
9f73d23ee4
36 changed files with 1335 additions and 699 deletions
|
@ -26,6 +26,7 @@ project.ext.excludeFromParallelIntegrationTests = true
|
|||
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
runtimeOnly project(":CodeCompare")
|
||||
|
||||
testImplementation project(path: ':Project', configuration: 'testArtifacts')
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
|
|
|
@ -315,9 +315,56 @@
|
|||
|
||||
<P align="left"><A name="Clear_Match"></A>The <b>Clear Match</b> <IMG src="images/undo-apply.png" border="0">
|
||||
action will reset the match to unaccepted and undo any applied markup.</P>
|
||||
|
||||
|
||||
|
||||
<P align="left"><A name="Remove_Match"></A>The <b>Remove Match</b> <IMG src="images/edit-delete.png" border="0">
|
||||
action will remove a manually created match from the matches table.</P>
|
||||
action will remove the selected match(es).</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="help/shared/warning.png" alt="Note" border="0">
|
||||
|
||||
As of Ghidra 11.2, Version Tracking supports deleting matches. Any match that has
|
||||
not been accepted can be deleted without confirmation. However, if you attempt to
|
||||
delete an <B>accepted match</B> that is the <B>last match for an association</B>, then
|
||||
you will be prompted to confirm your decision.
|
||||
</P>
|
||||
<P>
|
||||
Generally, we suggest users should not delete accepted matches. The more matches that
|
||||
are accepted, the better the Version Tracking results, since user choices affect
|
||||
future match scores. Keeping accepted matches and the applied markup provides
|
||||
future analysis with more corroborating details. Contrastingly, deleting accepted
|
||||
matches while keeping applied markup will remove supporting evidence that the user has
|
||||
already substantiated.
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="help/shared/tip.png">An alternative to deleting matches is to
|
||||
simply filter them out of the table once they have been applied. You can also tag
|
||||
any matches you wish to ignore and then use the <A HREF="#VT_Advanced_Filters">
|
||||
advanced filters</a> to hide any matches with those tags.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
<P>
|
||||
It is important to understand what happens in Version Tracking when deleting a match.
|
||||
You will have to make a decision before deleting whether you want to keep any changes
|
||||
made to the destination program when you accepted a given match or whether you wish to
|
||||
remove that markup. When deleting an accepted match:
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<U>To keep all applied markup</U>, simply delete the match and, when
|
||||
prompted, choose <B>Delete Accepted Matches</B>. This choice will delete the match and
|
||||
its markup items, but <B>any applied markup item content will remain in the destination
|
||||
program.</B> Alternatively, when prompted, you can choose <B>Finish</B> which will
|
||||
close the prompt dialog and will not delete the remaining accepted matches or markup.
|
||||
<P>
|
||||
<U>To remove all applied markup</U>, then you must first
|
||||
<A HREF="#Clear_Match">clear</A> the match before executing the remove action. The clear
|
||||
action will remove applied markup. After clearing the match, then you can remove
|
||||
the match and no markup will remain in the destination program.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P align="left"><A name="Make_Selections"></A>The <b>Make Selections</b> <IMG src="Icons.MAKE_SELECTION_ICON" border="0">
|
||||
action will create selections in the source and destination tools for all matches selected in the table.</P>
|
||||
|
@ -400,79 +447,101 @@
|
|||
|
||||
<H2><A name="Match_Filters"></A>Match Filters</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P align="left">The match table has an extensive assortment of filters. There
|
||||
are several commonly used filter controls at the bottom of the table:
|
||||
<ol>
|
||||
<li><b>Text Filter</b> - allows you to filter based on any text in the table
|
||||
</li>
|
||||
<li><b>Score Filter</b> - allows you to filter on a range of scores. All scores
|
||||
are between 0 and 1
|
||||
</li>
|
||||
<li><b>Confidence Filter</b> - allows you to filter a range of confidence values.
|
||||
All confidence values will be greater than -9.999 and smaller than 9.999.
|
||||
</li>
|
||||
<li><b>Length Filter</b> - is used to filter out
|
||||
functions that are smaller than some number
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</P>
|
||||
|
||||
<P>Finally, the <IMG src="images/view-filter.png" border="0"> will show the ancillary filters
|
||||
available. The table below lists and describes the available filters. When an ancillary
|
||||
filter is applied, the icon will change to <IMG src="images/lightbulb.png" border="0"> .
|
||||
Further, the icon may occasionally flash as a reminder that there is a filter applied.</P>
|
||||
<BR>
|
||||
<TABLE border="1" width="90%">
|
||||
<TR>
|
||||
<TH nowrap>Filter Name</TH>
|
||||
|
||||
<TH>Description</TH>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top">Match Type</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only function or data matches.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top" nowrap>Association Status</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose assocation
|
||||
has one of the included status types. A useful setting
|
||||
for this filter is to turn off all but the <B>Available</B> status. This will cause the
|
||||
table to act like a "To Do" list.</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Symbol Type</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose source or
|
||||
destination labels are of one of the included symbol types.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top">Algorithms</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches that were generated
|
||||
by one of the included types of correlating algorithms</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Address Range</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose source or
|
||||
destination address is within the specified range.</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Tags</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose tag is an
|
||||
included tag.</TD>
|
||||
</TR>
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H3>Table Filters</H3>
|
||||
|
||||
|
||||
</TABLE>
|
||||
<BLOCKQUOTE>
|
||||
<P align="left">The match table has an extensive assortment of filters. There
|
||||
are several commonly used filter controls at the bottom of the table:
|
||||
<ol>
|
||||
<li><b>Text Filter</b> - allows you to filter based on any text in the table
|
||||
</li>
|
||||
<li><b>Score Filter</b> - allows you to filter on a range of scores. All scores
|
||||
are between 0 and 1
|
||||
</li>
|
||||
<li><b>Confidence Filter</b> - allows you to filter a range of confidence values.
|
||||
All confidence values will be greater than -9.999 and smaller than 9.999.
|
||||
</li>
|
||||
<li><b>Length Filter</b> - is used to filter out
|
||||
functions that are smaller than some number
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<A NAME="VT_Advanced_Filters"></A>
|
||||
<H3>Advanced Filters</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<P>Finally, the <IMG src="icon.version.tracking.unfiltered" border="0"> will show the ancillary filters
|
||||
available. The table below lists and describes the available filters. When an ancillary
|
||||
filter is applied, the icon will change to <IMG src="icon.version.tracking.filtered" border="0"> .
|
||||
Further, the icon may occasionally flash as a reminder that there is a filter applied.</P>
|
||||
<BR>
|
||||
<TABLE border="1" width="90%">
|
||||
<TR>
|
||||
<TH nowrap>Filter Name</TH>
|
||||
|
||||
<TH>Description</TH>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top">Match Type</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only function or data matches.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top" nowrap>Association Status</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose assocation
|
||||
has one of the included status types. A useful setting
|
||||
for this filter is to turn off all but the <B>Available</B> status. This will cause the
|
||||
table to act like a "To Do" list.</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Symbol Type</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose source or
|
||||
destination labels are of one of the included symbol types.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top">Algorithms</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches that were generated
|
||||
by one of the included types of correlating algorithms</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Address Range</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose source or
|
||||
destination address is within the specified range.</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top">Tags</TD>
|
||||
|
||||
<TD valign="top">This filter allows the user to show only matches whose tag is an
|
||||
included tag.</TD>
|
||||
</TR>
|
||||
|
||||
|
||||
</TABLE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Table Column Filters</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
The matches table also supports
|
||||
<A HREF="help/topics/Trees/GhidraTreeFilter.html#Column_Filters">
|
||||
Table Column Filters</A> for creating complex filters for individual table columns.
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE> <!-- end of top-level blockquote -->
|
||||
|
|
|
@ -21,8 +21,7 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
|
||||
import db.*;
|
||||
import ghidra.feature.vt.api.impl.MarkupItemStorage;
|
||||
import ghidra.feature.vt.api.impl.VTEvent;
|
||||
import ghidra.feature.vt.api.impl.*;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.api.util.VTAssociationStatusException;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
|
@ -138,33 +137,13 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||
return createMarkupItemDB(markupItemStorage);
|
||||
}
|
||||
|
||||
void removeMarkupItem(MarkupItemStorageDB appliedMarkupItemDB) {
|
||||
// non-interface method; internal API use
|
||||
public void removeStoredMarkupItems(List<MarkupItemImpl> impls) {
|
||||
|
||||
VTAssociationDB association = (VTAssociationDB) appliedMarkupItemDB.getAssociation();
|
||||
|
||||
validateAcceptedState(appliedMarkupItemDB, association);
|
||||
|
||||
try {
|
||||
markupItemTableAdapter.removeMatchMarkupItemRecord(appliedMarkupItemDB.getKey());
|
||||
}
|
||||
catch (IOException e) {
|
||||
session.dbError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateAcceptedState(MarkupItemStorageDB appliedItem,
|
||||
VTAssociationDB association) {
|
||||
//
|
||||
// For any 'applied' markup item we assume that its association will be 'ACCEPTED'. The
|
||||
// exception to this rule is when we have markup items in the database, but that are not
|
||||
// applied (like when we change the destination address without applying)
|
||||
//
|
||||
VTAssociationStatus associationStatus = association.getStatus();
|
||||
VTMarkupItemStatus status = appliedItem.getStatus();
|
||||
if (status.isUnappliable()) {
|
||||
if (associationStatus != ACCEPTED) {
|
||||
throw new AssertException("Cannot have an applied markup item with an " +
|
||||
"association that is not ACCEPTED");
|
||||
for (MarkupItemImpl impl : impls) {
|
||||
MarkupItemStorage storage = impl.getStorage();
|
||||
if (storage instanceof MarkupItemStorageDB storageDb) {
|
||||
removeMarkupRecord(storageDb.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,17 +230,31 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||
}
|
||||
|
||||
void removeAssociation(VTAssociation association) {
|
||||
VTAssociationDB existingAssociation = (VTAssociationDB) association;
|
||||
long id = existingAssociation.getKey();
|
||||
|
||||
// Update the association status so that we update any blocked associations
|
||||
VTAssociationDB associationDB = (VTAssociationDB) association;
|
||||
VTAssociationStatus status = association.getStatus();
|
||||
if (status == ACCEPTED) {
|
||||
associationDB.setStatus(AVAILABLE);
|
||||
associationDB.setInvalid();
|
||||
unblockRelatedAssociations(associationDB);
|
||||
for (AssociationHook hook : associationHooks) {
|
||||
hook.associationCleared(associationDB);
|
||||
}
|
||||
}
|
||||
|
||||
VTAssociationDB associationDb = (VTAssociationDB) association;
|
||||
long id = associationDb.getKey();
|
||||
try {
|
||||
associationDb.removeMarkupItems();
|
||||
associationTableAdapter.removeAssociaiton(id);
|
||||
session.setChanged(VTEvent.ASSOCIATION_REMOVED, existingAssociation, null);
|
||||
session.setChanged(VTEvent.ASSOCIATION_REMOVED, associationDb, null);
|
||||
}
|
||||
catch (IOException e) {
|
||||
session.dbError(e);
|
||||
}
|
||||
associationCache.delete(id);
|
||||
existingAssociation.setInvalid();
|
||||
associationDb.setInvalid();
|
||||
|
||||
}
|
||||
|
||||
|
@ -507,7 +500,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||
throws VTAssociationStatusException {
|
||||
if (association.hasAppliedMarkupItems()) {
|
||||
throw new VTAssociationStatusException(
|
||||
"VTMarkupItemManager contains applied " + "markup items");
|
||||
"VTMarkupItemManager contains applied markup items");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,9 +616,9 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||
associationHooks.remove(hook);
|
||||
}
|
||||
|
||||
void removeMarkupRecord(DBRecord record) {
|
||||
void removeMarkupRecord(long key) {
|
||||
try {
|
||||
markupItemTableAdapter.removeMatchMarkupItemRecord(record.getKey());
|
||||
markupItemTableAdapter.removeMarkupItemRecord(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
session.dbError(e);
|
||||
|
|
|
@ -135,7 +135,7 @@ public class MarkupItemStorageDB extends DatabaseObject implements MarkupItemSto
|
|||
try {
|
||||
MarkupItemStorage storage = new MarkupItemStorageImpl(getAssociation(), getMarkupType(),
|
||||
getSourceAddress(), getDestinationAddress(), getDestinationAddressSource());
|
||||
associationManager.removeMarkupRecord(record);
|
||||
associationManager.removeMarkupRecord(record.getKey());
|
||||
return storage;
|
||||
}
|
||||
finally {
|
||||
|
@ -144,7 +144,8 @@ public class MarkupItemStorageDB extends DatabaseObject implements MarkupItemSto
|
|||
}
|
||||
|
||||
@Override
|
||||
public MarkupItemStorage setDestinationAddress(Address destinationAddress, String addressSource) {
|
||||
public MarkupItemStorage setDestinationAddress(Address destinationAddress,
|
||||
String addressSource) {
|
||||
if (destinationAddress == null) {
|
||||
destinationAddress = Address.NO_ADDRESS;
|
||||
}
|
||||
|
|
|
@ -296,4 +296,8 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||
public boolean hasAppliedMarkupItems() {
|
||||
return markupManager.hasAppliedMarkupItems();
|
||||
}
|
||||
|
||||
void removeMarkupItems() {
|
||||
markupManager.removeMarkupItems();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public abstract class VTMatchMarkupItemTableDBAdapter {
|
|||
|
||||
public abstract RecordIterator getRecords() throws IOException;
|
||||
|
||||
public abstract void removeMatchMarkupItemRecord(long key) throws IOException;
|
||||
public abstract void removeMarkupItemRecord(long key) throws IOException;
|
||||
|
||||
public abstract DBRecord getRecord(long key) throws IOException;
|
||||
|
||||
|
@ -70,5 +70,6 @@ public abstract class VTMatchMarkupItemTableDBAdapter {
|
|||
|
||||
public abstract int getRecordCount();
|
||||
|
||||
public abstract DBRecord createMarkupItemRecord(MarkupItemStorage markupItem) throws IOException;
|
||||
public abstract DBRecord createMarkupItemRecord(MarkupItemStorage markupItem)
|
||||
throws IOException;
|
||||
}
|
||||
|
|
|
@ -15,14 +15,11 @@
|
|||
*/
|
||||
package ghidra.feature.vt.api.db;
|
||||
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.ADDRESS_SOURCE_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.ASSOCIATION_KEY_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.DESTINATION_ADDRESS_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.MARKUP_TYPE_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.ORIGINAL_DESTINATION_VALUE_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.SOURCE_ADDRESS_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.SOURCE_VALUE_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.STATUS_COL;
|
||||
import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.feature.vt.api.impl.MarkupItemStorage;
|
||||
import ghidra.feature.vt.api.main.VTSession;
|
||||
import ghidra.feature.vt.api.markuptype.VTMarkupTypeFactory;
|
||||
|
@ -34,10 +31,6 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
|
||||
public class VTMatchMarkupItemTableDBAdapterV0 extends VTMatchMarkupItemTableDBAdapter {
|
||||
|
||||
private Table table;
|
||||
|
@ -71,20 +64,20 @@ public class VTMatchMarkupItemTableDBAdapterV0 extends VTMatchMarkupItemTableDBA
|
|||
|
||||
record.setLongValue(ASSOCIATION_KEY_COL.column(), association.getKey());
|
||||
record.setString(ADDRESS_SOURCE_COL.column(), markupItem.getDestinationAddressSource());
|
||||
record.setLongValue(SOURCE_ADDRESS_COL.column(), getAddressID(sourceProgram,
|
||||
markupItem.getSourceAddress()));
|
||||
record.setLongValue(SOURCE_ADDRESS_COL.column(),
|
||||
getAddressID(sourceProgram, markupItem.getSourceAddress()));
|
||||
|
||||
Address destinationAddress = markupItem.getDestinationAddress();
|
||||
if (destinationAddress != null) {
|
||||
record.setLongValue(DESTINATION_ADDRESS_COL.column(), getAddressID(destinationProgram,
|
||||
markupItem.getDestinationAddress()));
|
||||
record.setLongValue(DESTINATION_ADDRESS_COL.column(),
|
||||
getAddressID(destinationProgram, markupItem.getDestinationAddress()));
|
||||
}
|
||||
record.setShortValue(MARKUP_TYPE_COL.column(),
|
||||
(short) VTMarkupTypeFactory.getID(markupItem.getMarkupType()));
|
||||
record.setString(SOURCE_VALUE_COL.column(), Stringable.getString(
|
||||
markupItem.getSourceValue(), sourceProgram));
|
||||
record.setString(ORIGINAL_DESTINATION_VALUE_COL.column(), Stringable.getString(
|
||||
markupItem.getDestinationValue(), destinationProgram));
|
||||
record.setString(SOURCE_VALUE_COL.column(),
|
||||
Stringable.getString(markupItem.getSourceValue(), sourceProgram));
|
||||
record.setString(ORIGINAL_DESTINATION_VALUE_COL.column(),
|
||||
Stringable.getString(markupItem.getDestinationValue(), destinationProgram));
|
||||
record.setByteValue(STATUS_COL.column(), (byte) markupItem.getStatus().ordinal());
|
||||
|
||||
table.putRecord(record);
|
||||
|
@ -97,7 +90,7 @@ public class VTMatchMarkupItemTableDBAdapterV0 extends VTMatchMarkupItemTableDBA
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeMatchMarkupItemRecord(long key) throws IOException {
|
||||
public void removeMarkupItemRecord(long key) throws IOException {
|
||||
table.deleteRecord(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@ import org.jdom.JDOMException;
|
|||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
import db.*;
|
||||
import ghidra.feature.vt.api.correlator.program.ImpliedMatchProgramCorrelator;
|
||||
import ghidra.feature.vt.api.correlator.program.ManualMatchProgramCorrelator;
|
||||
import ghidra.feature.vt.api.impl.*;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
|
@ -186,37 +184,47 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||
|
||||
@Override
|
||||
public boolean removeMatch(VTMatch match) {
|
||||
if (!(match instanceof VTMatchDB)) {
|
||||
return false;
|
||||
}
|
||||
if (!match.getMatchSet().hasRemovableMatches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VTMatchDB matchDB = (VTMatchDB) match;
|
||||
if (!(match instanceof VTMatchDB matchDb)) {
|
||||
// this should not be possible from the UI
|
||||
throw new IllegalArgumentException("Can only remove matches saved to the database");
|
||||
}
|
||||
|
||||
VTAssociation association = match.getAssociation();
|
||||
|
||||
// Remove the association if it was the only remaining match for that association.
|
||||
AssociationDatabaseManager associationManager = session.getAssociationManagerDBM();
|
||||
List<VTMatch> matches = session.getMatches(association);
|
||||
if (matches.size() == 1 && association.getStatus() == VTAssociationStatus.ACCEPTED) {
|
||||
return false; // can't remove the last match if the association is accepted
|
||||
// This method prevents deleting the association if it is accepted, as it would cause
|
||||
// the user to lose potentially valuable information without realizing it. To work
|
||||
// around that issue when calling this method, the user can first un-accept the match.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the match record
|
||||
deleteMatch(matchDb);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMatch(VTMatch match) {
|
||||
if (!(match instanceof VTMatchDB matchDb)) {
|
||||
// this should not be possible from the UI
|
||||
throw new IllegalArgumentException("Can only remove matches saved to the database");
|
||||
}
|
||||
|
||||
VTAssociation association = match.getAssociation();
|
||||
Address sourceAddress = association.getSourceAddress();
|
||||
Address destinationAddress = association.getDestinationAddress();
|
||||
try {
|
||||
lock.acquire();
|
||||
long matchKey = matchDB.getKey();
|
||||
long matchKey = matchDb.getKey();
|
||||
boolean deleted = matchTableAdapter.deleteRecord(matchKey);
|
||||
if (deleted) {
|
||||
matchCache.delete(matchKey);
|
||||
|
||||
if (matches.size() == 1) {
|
||||
List<VTMatch> matches = session.getMatches(association);
|
||||
if (matches.isEmpty()) {
|
||||
// if last match, remove association
|
||||
associationManager.removeAssociation(association);
|
||||
AssociationDatabaseManager manager = session.getAssociationManagerDBM();
|
||||
manager.removeAssociation(association);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +237,6 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||
|
||||
DeletedMatch deletedMatch = new DeletedMatch(sourceAddress, destinationAddress);
|
||||
session.setObjectChanged(VTEvent.MATCH_DELETED, match, deletedMatch, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -349,14 +356,6 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRemovableMatches() {
|
||||
VTProgramCorrelatorInfo info = getProgramCorrelatorInfo();
|
||||
String correlatorClassName = info.getCorrelatorClassName();
|
||||
return correlatorClassName.equals(ManualMatchProgramCorrelator.class.getName()) ||
|
||||
correlatorClassName.equals(ImpliedMatchProgramCorrelator.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Match Set " + getID() + " - " + getMatchCount() + " matches [Correlator=" +
|
||||
|
|
|
@ -501,4 +501,8 @@ public class MarkupItemImpl implements VTMarkupItem {
|
|||
newStatus);
|
||||
}
|
||||
|
||||
// non-interface method
|
||||
public MarkupItemStorage getStorage() {
|
||||
return markupItemStorage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
*/
|
||||
package ghidra.feature.vt.api.impl;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.feature.vt.api.db.*;
|
||||
import ghidra.feature.vt.api.main.VTMarkupItem;
|
||||
import ghidra.feature.vt.api.main.VTMarkupItemStatus;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MarkupItemManagerImpl {
|
||||
|
||||
|
@ -70,7 +70,7 @@ public class MarkupItemManagerImpl {
|
|||
return Collections.unmodifiableList(markupItems);
|
||||
}
|
||||
|
||||
protected List<VTMarkupItem> createMarkupItems(TaskMonitor monitor) throws CancelledException {
|
||||
private List<VTMarkupItem> createMarkupItems(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
Collection<VTMarkupItem> generatedMarkupItems = getGeneratedMarkupItems(monitor);
|
||||
Collection<VTMarkupItem> databaseMarkupItems = getStoredMarkupItems(monitor);
|
||||
|
@ -104,18 +104,6 @@ public class MarkupItemManagerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
private Collection<VTMarkupItem> getStoredMarkupItems(TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
AssociationDatabaseManager associationDBM = association.getAssociationManagerDB();
|
||||
Collection<MarkupItemStorageDB> appliedMarkupItems =
|
||||
associationDBM.getAppliedMarkupItems(monitor, association);
|
||||
List<VTMarkupItem> list = new ArrayList<VTMarkupItem>();
|
||||
for (MarkupItemStorageDB markupItemStorageDB : appliedMarkupItems) {
|
||||
list.add(new MarkupItemImpl(markupItemStorageDB));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<VTMarkupItem> replaceGeneratedMarkupItemsWithDBMarkupItems(
|
||||
Collection<VTMarkupItem> generatedMarkupItems,
|
||||
Collection<VTMarkupItem> databaseMarkupItems) {
|
||||
|
@ -142,8 +130,45 @@ public class MarkupItemManagerImpl {
|
|||
markupItem.getSourceAddress().toString(true);
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
// synchronized due to write of 'markupItems'
|
||||
public synchronized void clearCache() {
|
||||
markupItems = EMPTY_LIST;
|
||||
}
|
||||
|
||||
private Collection<VTMarkupItem> getStoredMarkupItems(TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
AssociationDatabaseManager associationDBM = association.getAssociationManagerDB();
|
||||
Collection<MarkupItemStorageDB> appliedMarkupItems =
|
||||
associationDBM.getAppliedMarkupItems(monitor, association);
|
||||
List<VTMarkupItem> list = new ArrayList<VTMarkupItem>();
|
||||
for (MarkupItemStorageDB markupItemStorageDB : appliedMarkupItems) {
|
||||
list.add(new MarkupItemImpl(markupItemStorageDB));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// synchronized to match getMarkupItems() so we do not have other clients loading items while
|
||||
// we are processing them
|
||||
public synchronized void removeMarkupItems() {
|
||||
|
||||
List<VTMarkupItem> items;
|
||||
try {
|
||||
items = getMarkupItems(TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return; // can't happen with DUMMY
|
||||
}
|
||||
|
||||
List<MarkupItemImpl> impls = items.stream()
|
||||
.map(item -> (MarkupItemImpl) item)
|
||||
.filter(impl -> impl.isStoredInDB())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
AssociationDatabaseManager associationDbm = association.getAssociationManagerDB();
|
||||
associationDbm.removeStoredMarkupItems(impls);
|
||||
|
||||
// signal that markup item info has changed and must be reloaded when next needed
|
||||
clearCache();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
*/
|
||||
package ghidra.feature.vt.api.impl;
|
||||
|
||||
import ghidra.feature.vt.api.correlator.program.ImpliedMatchProgramCorrelator;
|
||||
import ghidra.feature.vt.api.correlator.program.ManualMatchProgramCorrelator;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MatchSetImpl implements VTMatchSet {
|
||||
private ProgramCorrelatorInfoFake correlatorInfo;
|
||||
private final VTSession session;
|
||||
|
@ -77,11 +75,8 @@ public class MatchSetImpl implements VTMatchSet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRemovableMatches() {
|
||||
VTProgramCorrelatorInfo info = getProgramCorrelatorInfo();
|
||||
String correlatorClassName = info.getCorrelatorClassName();
|
||||
return correlatorClassName.equals(ManualMatchProgramCorrelator.class.getName()) ||
|
||||
correlatorClassName.equals(ImpliedMatchProgramCorrelator.class.getName());
|
||||
public void deleteMatch(VTMatch match) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.feature.vt.api.main;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.feature.vt.api.impl.VTProgramCorrelatorInfo;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Interface for all the matches generated from a single program correlator run.
|
||||
*
|
||||
|
@ -57,14 +56,14 @@ public interface VTMatchSet {
|
|||
|
||||
/**
|
||||
* Returns the number of matches contained in this match set.
|
||||
* @return
|
||||
* @return the number of matches contained in this match set.
|
||||
*/
|
||||
public int getMatchCount();
|
||||
|
||||
/**
|
||||
* Returns a unique id for this match set. The ids are one-up numbers indicating the order this
|
||||
* match set was generated in relation to other match sets in the VTSession.
|
||||
* @return
|
||||
* @return the id
|
||||
*/
|
||||
public int getID();
|
||||
|
||||
|
@ -72,7 +71,7 @@ public interface VTMatchSet {
|
|||
* Returns a collection of all matches for the given association.
|
||||
* @param association the association for which to search for matches.
|
||||
* @return a collection of all matches for the given association.
|
||||
* @see #getMatches(Address, Address, VTAssociationType)
|
||||
* @see #getMatches(Address, Address)
|
||||
*/
|
||||
public Collection<VTMatch> getMatches(VTAssociation association);
|
||||
|
||||
|
@ -90,16 +89,51 @@ public interface VTMatchSet {
|
|||
public Collection<VTMatch> getMatches(Address sourceAddress, Address destinationAddress);
|
||||
|
||||
/**
|
||||
* Removes a match from this match set. Note that this operation is only supported for built-in
|
||||
* match sets "Manual Matches" and "Implied Matches".
|
||||
* Deletes the given match from this match set.
|
||||
* <P>
|
||||
* Note: deleting an <B>ACCEPTED</B> match removes potentially useful corroborating evidence
|
||||
* from future correlation. Before deleting a match, consider instead filtering matches out of
|
||||
* the UI that you are finished applying.
|
||||
* <P>
|
||||
* If this is the last match that shares the match's association, then the association will also
|
||||
* be removed, along with any markup items in the database. <B>Any applied markup item data
|
||||
* will not be changed.</B>
|
||||
*
|
||||
* @param match the match
|
||||
*/
|
||||
public void deleteMatch(VTMatch match);
|
||||
|
||||
/**
|
||||
* Removes a match from this match set.
|
||||
* <P>
|
||||
* If this is the last match that shares the match's association, then the match will only be
|
||||
* removed if the association is not accepted. In that case, no remove will take place and
|
||||
* this method will return false.
|
||||
* <P>
|
||||
* Note: This method is deprecated. It unfortunately shares a very similar name with its
|
||||
* replacement, {@link #deleteMatch(VTMatch)}. The replacement method will delete the match
|
||||
* and the related association and markup items in the database, if the match is the last match
|
||||
* to use that association. This deprecated method does not remove the remaining association or
|
||||
* markup items. Historically, this method has been called after clearing the given match and
|
||||
* its markup. Once this method has been deleted, clients will be responsible for managing the
|
||||
* markup item state before calling {@link #deleteMatch(VTMatch)}.
|
||||
*
|
||||
* @param match the match to remove.
|
||||
* @return true if the match was removed.
|
||||
* @throws IllegalArgumentException if a non-database match is passed to this method
|
||||
* @see #deleteMatch(VTMatch)
|
||||
* @deprecated use {@link #deleteMatch(VTMatch)}
|
||||
*/
|
||||
@Deprecated(since = "11.2", forRemoval = true)
|
||||
public boolean removeMatch(VTMatch match);
|
||||
|
||||
/**
|
||||
* Returns true if this match set supports removing matches.
|
||||
* @return true if this match set supports removing matches.
|
||||
* Returns true
|
||||
* @return true
|
||||
* @deprecated this method now always returns true
|
||||
*/
|
||||
public boolean hasRemovableMatches();
|
||||
@Deprecated(since = "11.2", forRemoval = true)
|
||||
public default boolean hasRemovableMatches() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ public class RemoveMatchAction extends DockingAction {
|
|||
super("Remove", VTPlugin.OWNER);
|
||||
this.controller = controller;
|
||||
|
||||
// setToolBarData(new ToolBarData(ICON, MENU_GROUP));
|
||||
setPopupMenuData(new MenuData(new String[] { "Remove Match" }, ICON, MENU_GROUP));
|
||||
setEnabled(false);
|
||||
setHelpLocation(new HelpLocation("VersionTrackingPlugin", "Remove_Match"));
|
||||
|
@ -64,17 +63,7 @@ public class RemoveMatchAction extends DockingAction {
|
|||
}
|
||||
VTMatchContext matchContext = (VTMatchContext) context;
|
||||
List<VTMatch> matches = matchContext.getSelectedMatches();
|
||||
if (matches.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (!isRemovableMatch(matches.get(0))) {
|
||||
return false; // It must be a single manual match.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isRemovableMatch(VTMatch vtMatch) {
|
||||
return vtMatch.getMatchSet().hasRemovableMatches();
|
||||
return !matches.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -282,12 +282,6 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
|||
fireStatusChanged(getFilterStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
lowerAddressRangeTextField.setText(MIN_ADDRESS_VALUE.toString());
|
||||
upperAddressRangeTextField.setText(MAX_ADDRESS_VALUE.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return component;
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.feature.vt.gui.filters;
|
||||
|
||||
import static ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus.APPLIED;
|
||||
import static ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus.NONE;
|
||||
import static ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus.*;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.LayoutManager;
|
||||
|
@ -151,13 +150,6 @@ public abstract class CheckBoxBasedAncillaryFilter<T> extends AncillaryFilter<T>
|
|||
return FilterShortcutState.REQUIRES_CHECK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
for (CheckBoxInfo<T> info : checkBoxInfos) {
|
||||
info.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterState getFilterState() {
|
||||
FilterState state = new FilterState(this);
|
||||
|
|
|
@ -46,8 +46,6 @@ public abstract class Filter<T> {
|
|||
|
||||
public abstract FilterEditingStatus getFilterStatus();
|
||||
|
||||
public abstract void clearFilter();
|
||||
|
||||
public abstract JComponent getComponent();
|
||||
|
||||
public void dispose() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,12 +17,13 @@ package ghidra.feature.vt.gui.filters;
|
|||
|
||||
public interface FilterDialogModel<T> {
|
||||
|
||||
public void addFilter( Filter<T> filter );
|
||||
|
||||
public void forceRefilter();
|
||||
|
||||
/**
|
||||
* Will be called when the visibility of the dialog using this model has changed
|
||||
*/
|
||||
public void dialogVisibilityChanged( boolean isVisible );
|
||||
public void addFilter(Filter<T> filter);
|
||||
|
||||
public void forceRefilter();
|
||||
|
||||
/**
|
||||
* Will be called when the visibility of the dialog using this model has changed
|
||||
* @param isVisible true if visible
|
||||
*/
|
||||
public void dialogVisibilityChanged(boolean isVisible);
|
||||
}
|
||||
|
|
|
@ -243,12 +243,6 @@ public class TagFilter extends AncillaryFilter<VTMatch> {
|
|||
excludedTags = getTagsFromText(tagText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
excludedTags.clear();
|
||||
excludedTagsLabel.setText(ALL_TAGS_INCLUDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return component;
|
||||
|
|
|
@ -150,13 +150,14 @@ public class VTPlugin extends Plugin {
|
|||
|
||||
private void addCustomPlugins() {
|
||||
|
||||
List<String> names = new ArrayList<>(List.of("ghidra.features.codecompare.plugin"));
|
||||
List<String> names =
|
||||
new ArrayList<>(List.of("ghidra.features.codecompare.plugin.FunctionComparisonPlugin"));
|
||||
List<Plugin> plugins = tool.getManagedPlugins();
|
||||
Set<String> existingNames =
|
||||
plugins.stream().map(c -> c.getName()).collect(Collectors.toSet());
|
||||
|
||||
// Note: we check to see if the plugins we want to add have already been added to the tool.
|
||||
// We should not needed to do this, but once the tool has been saved with the plugins added,
|
||||
// We should not need to do this, but once the tool has been saved with the plugins added,
|
||||
// they will get added again the next time the tool is loaded. Adding this check here seems
|
||||
// easier than modifying the default to file to load the plugins, since the amount of xml
|
||||
// required for that is non-trivial.
|
||||
|
|
|
@ -127,12 +127,6 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
|||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
lowerBoundField.setText(minValue.toString());
|
||||
upperBoundField.setText(maxValue.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
FilterEditingStatus lowerStatus = lowerBoundField.getFilterStatus();
|
||||
|
|
|
@ -89,11 +89,6 @@ public class LengthFilter extends Filter<VTMatch> {
|
|||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
textField.setText(DEFAULT_FILTER_VALUE.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
return textField.getFilterStatus();
|
||||
|
|
|
@ -34,6 +34,8 @@ import javax.swing.table.*;
|
|||
import docking.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.columnfilter.ColumnBasedTableFilter;
|
||||
import docking.widgets.table.columnfilter.ColumnFilterManager;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
|
@ -80,6 +82,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
|||
|
||||
private AncillaryFilterDialogComponentProvider<VTMatch> ancillaryFilterDialog;
|
||||
private JButton ancillaryFilterButton;
|
||||
private ColumnFilterManager<VTMatch> columnFilterManager;
|
||||
private VTColumnFilter vtColumnFilter;
|
||||
|
||||
private FilterIconFlashTimer<VTMatch> iconTimer;
|
||||
private Set<Filter<VTMatch>> filters = new HashSet<>();
|
||||
|
@ -386,8 +390,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
|||
JPanel innerPanel = new JPanel(new HorizontalLayout(4));
|
||||
innerPanel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
||||
|
||||
JComponent nameFilterPanel = createTextFilterPanel();
|
||||
parentPanel.add(nameFilterPanel, BorderLayout.CENTER);
|
||||
JComponent textFilterPanel = createTextFilterPanel();
|
||||
parentPanel.add(textFilterPanel, BorderLayout.CENTER);
|
||||
parentPanel.add(innerPanel, BorderLayout.EAST);
|
||||
|
||||
JComponent scoreFilterPanel = createScoreFilterPanel();
|
||||
|
@ -409,13 +413,33 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
|||
helpService.registerHelp(parentPanel, filterHelpLocation);
|
||||
helpService.registerHelp(ancillaryFilterButton, filterHelpLocation);
|
||||
|
||||
JButton columnFilterButton = createColumnFilterButton();
|
||||
innerPanel.add(columnFilterButton);
|
||||
|
||||
innerPanel.add(ancillaryFilterButton);
|
||||
|
||||
return parentPanel;
|
||||
}
|
||||
|
||||
private JButton createColumnFilterButton() {
|
||||
|
||||
String preferenceKey =
|
||||
matchesTable.getPreferenceKey() + ColumnFilterManager.FILTER_EXTENSION;
|
||||
columnFilterManager = new ColumnFilterManager<VTMatch>(matchesTable, matchesTableModel,
|
||||
preferenceKey, this::updateColumnFilter);
|
||||
|
||||
vtColumnFilter = new VTColumnFilter(columnFilterManager.getCurrentFilter());
|
||||
addFilter(vtColumnFilter);
|
||||
|
||||
return columnFilterManager.getConfigureButton();
|
||||
}
|
||||
|
||||
private void updateColumnFilter() {
|
||||
vtColumnFilter.setFilter(columnFilterManager.getCurrentFilter());
|
||||
refilter();
|
||||
}
|
||||
|
||||
private JComponent createTextFilterPanel() {
|
||||
// MatchNameFilter nameFilterPanel = new MatchNameFilter(controller, matchesTable);
|
||||
AllTextFilter<VTMatch> allTextFilter =
|
||||
new AllTextFilter<>(controller, matchesTable, matchesTableModel);
|
||||
allTextFilter.setName(TEXT_FILTER_NAME);
|
||||
|
@ -506,6 +530,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
ancillaryFilterDialog.dispose();
|
||||
|
||||
columnFilterManager.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1069,4 +1095,73 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
|||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
private class VTColumnFilter extends Filter<VTMatch> {
|
||||
|
||||
private ColumnBasedTableFilter<VTMatch> columnFilter;
|
||||
|
||||
VTColumnFilter(ColumnBasedTableFilter<VTMatch> columnFilter) {
|
||||
this.columnFilter = columnFilter;
|
||||
}
|
||||
|
||||
void setFilter(ColumnBasedTableFilter<VTMatch> columnFilter) {
|
||||
this.columnFilter = columnFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passesFilter(VTMatch t) {
|
||||
if (columnFilter == null) {
|
||||
return true;
|
||||
}
|
||||
return columnFilter.acceptsRow(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
if (columnFilter == null || columnFilter.isEmpty()) {
|
||||
return FilterEditingStatus.NONE;
|
||||
}
|
||||
|
||||
return FilterEditingStatus.APPLIED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
// This filter is configured outside of the VT filter API
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterShortcutState getFilterShortcutState() {
|
||||
return FilterShortcutState.REQUIRES_CHECK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter<VTMatch> createCopy() {
|
||||
return this; // does not currently support copying; should not be needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
// handled by the column filter manager
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
// handled by the column filter manager
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubFilterOf(Filter<VTMatch> otherFilter) {
|
||||
if (columnFilter == null) {
|
||||
return false;
|
||||
}
|
||||
if (otherFilter instanceof VTColumnFilter otherColumnFilter) {
|
||||
return columnFilter.isSubFilterOf(otherColumnFilter.columnFilter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
*/
|
||||
package ghidra.feature.vt.gui.task;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.feature.vt.api.db.VTMatchSetDB;
|
||||
import ghidra.feature.vt.api.main.VTMatch;
|
||||
import ghidra.feature.vt.api.main.VTSession;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -38,26 +40,75 @@ public class RemoveMatchTask extends VtTask {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean removeMatches(TaskMonitor monitor) throws CancelledException {
|
||||
private void removeMatches(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
monitor.setMessage("Removing matches");
|
||||
monitor.initialize(matches.size());
|
||||
boolean failed = false;
|
||||
for (VTMatch match : matches) {
|
||||
int n = matches.size();
|
||||
monitor.initialize(n);
|
||||
|
||||
//
|
||||
// First remove all matches that will not require user prompting (those that are not
|
||||
// accepted or they are not the last match for a shared association).
|
||||
//
|
||||
List<VTMatch> list = new ArrayList<>(matches); // create a mutable list
|
||||
Iterator<VTMatch> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
VTMatch match = it.next();
|
||||
VTMatchSetDB matchSet = (VTMatchSetDB) match.getMatchSet();
|
||||
boolean matchRemoved = matchSet.removeMatch(match);
|
||||
if (!matchRemoved) {
|
||||
failed = true;
|
||||
if (matchSet.removeMatch(match)) {
|
||||
it.remove();
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
monitor.setProgress(matches.size());
|
||||
if (failed) {
|
||||
reportError("One or more of your matches could not be removed." +
|
||||
"\nNote: You can't remove a match if it is currently accepted.");
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Now we have to ask the user if they wish to remove applied matches.
|
||||
//
|
||||
int delta = n - list.size();
|
||||
|
||||
//@formatter:off
|
||||
String message = """
|
||||
Deleted %d of %d matches.
|
||||
|
||||
The remaining %d matches are ACCEPTED. Do you wish to delete these matches and
|
||||
leave any applied destination program markup in place?
|
||||
(Press F1 to see more help details)
|
||||
""".formatted(delta, n, list.size());
|
||||
//@formatter:on
|
||||
|
||||
RemoveMatchDialog dialog = new RemoveMatchDialog(message);
|
||||
if (!dialog.promptToDelete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
VTMatch match = it.next();
|
||||
VTMatchSetDB matchSet = (VTMatchSetDB) match.getMatchSet();
|
||||
matchSet.deleteMatch(match);
|
||||
it.remove();
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private class RemoveMatchDialog extends OptionDialog {
|
||||
|
||||
RemoveMatchDialog(String message) {
|
||||
super("Delete ACCEPTED Matches?", message, "Delete Accepted Matches", "Finish",
|
||||
OptionDialog.QUESTION_MESSAGE, null, false);
|
||||
|
||||
setHelpLocation(new HelpLocation("VersionTrackingPlugin", "Remove_Match"));
|
||||
}
|
||||
|
||||
boolean promptToDelete() {
|
||||
int choice = super.show();
|
||||
return choice == OptionDialog.OPTION_ONE; // "Delete Accepted Matches"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,11 +125,6 @@ public abstract class AbstractTextFilter<T> extends Filter<T> {
|
|||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFilter() {
|
||||
textField.setText(defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
return textField.getFilterStatus();
|
||||
|
|
|
@ -115,7 +115,7 @@ public class ImpliedMatchUtils {
|
|||
VTMatchSet impliedMatchSet = session.getImpliedMatchSet();
|
||||
for (VTMatch vtMatch : matches) {
|
||||
if (vtMatch.getMatchSet() == impliedMatchSet) {
|
||||
impliedMatchSet.removeMatch(vtMatch);
|
||||
impliedMatchSet.deleteMatch(vtMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,8 +123,7 @@ public class ImpliedMatchUtils {
|
|||
/**
|
||||
* Method for finding version tracking implied matches given an accepted matched
|
||||
* function. Each referenced data and function that exist in equivalent sections
|
||||
* of the matched source and destination functions will added to the current
|
||||
* version tracking session as an implied match.
|
||||
* of the matched source and destination functions will be returned in the given set.
|
||||
*
|
||||
* @param sourceFunction The matched function from the source program
|
||||
* @param destinationFunction The matched function from the destination program
|
||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.feature.vt.api;
|
|||
import static ghidra.feature.vt.db.VTTestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
|
@ -27,9 +27,8 @@ import ghidra.feature.vt.api.main.VTAssociationStatus;
|
|||
import ghidra.feature.vt.api.main.VTMatch;
|
||||
import ghidra.feature.vt.gui.plugin.*;
|
||||
import ghidra.feature.vt.gui.task.AcceptMatchTask;
|
||||
import ghidra.feature.vt.gui.task.VtTask;
|
||||
import ghidra.feature.vt.gui.util.VTOptionDefines;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
|
@ -39,10 +38,7 @@ import ghidra.program.model.listing.*;
|
|||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
|
@ -53,13 +49,8 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private ProgramDB sourceProgram;
|
||||
private ProgramDB destinationProgram;
|
||||
private VTPlugin plugin;
|
||||
private DomainObjectListenerRecorder eventRecorder = new DomainObjectListenerRecorder();
|
||||
private Options options;
|
||||
|
||||
public VTMatchAcceptTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
|
@ -70,7 +61,6 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
ClassicSampleX86ProgramBuilder destinationBuilder = new ClassicSampleX86ProgramBuilder();
|
||||
destinationProgram = destinationBuilder.getProgram();
|
||||
destinationProgram.addListener(eventRecorder);
|
||||
|
||||
tool = env.getTool();
|
||||
|
||||
|
@ -84,29 +74,21 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
|
||||
options = controller.getOptions();
|
||||
options.setBoolean(VTOptionDefines.AUTO_CREATE_IMPLIED_MATCH, false);
|
||||
options.setBoolean(VTOptionDefines.APPLY_FUNCTION_NAME_ON_ACCEPT, false);
|
||||
options.setBoolean(VTOptionDefines.APPLY_DATA_NAME_ON_ACCEPT, false);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
waitForBusyTool(tool);
|
||||
destinationProgram.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
env.dispose();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptWithApplyDataLabels() throws Exception {
|
||||
|
||||
//
|
||||
// BTW this test exposes a bug because the hook that runs when you apply data on accept was.
|
||||
// in a side effect, causing the destination address to be set. When the hook was changed to
|
||||
// not set the destination address, the accept task was not setting the destination address
|
||||
// as it should.
|
||||
// This test exposes a bug because the hook that runs when you apply data on accept was
|
||||
// exhibiting a side effect, causing the destination address to be set. When the hook was
|
||||
// changed to not set the destination address, the accept task was not setting the
|
||||
// destination address as it should.
|
||||
//
|
||||
|
||||
options.setBoolean(VTOptionDefines.APPLY_DATA_NAME_ON_ACCEPT, true);
|
||||
|
@ -147,11 +129,9 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void runTask(Task task) throws CancelledException {
|
||||
|
||||
task.run(TaskMonitor.DUMMY);
|
||||
destinationProgram.flushEvents();
|
||||
waitForSwing();
|
||||
private void runTask(VtTask task) {
|
||||
controller.runVTTask(task);
|
||||
waitForProgram(destinationProgram);
|
||||
}
|
||||
|
||||
private Data setData(DataType dataType, int dtLength, Address address, Program program)
|
||||
|
@ -170,14 +150,4 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private class DomainObjectListenerRecorder implements DomainObjectListener {
|
||||
|
||||
List<DomainObjectChangedEvent> events = new ArrayList<DomainObjectChangedEvent>();
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
events.add(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ public class VTMatchApplyTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private ProgramDB destinationProgram;
|
||||
private VTPlugin plugin;
|
||||
|
||||
// TODO: debug
|
||||
private DomainObjectListenerRecorder eventRecorder = new DomainObjectListenerRecorder();
|
||||
|
||||
@Before
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
/* ###
|
||||
* 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.feature.vt.api;
|
||||
|
||||
import static ghidra.feature.vt.db.VTTestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.db.DummyTestProgramCorrelator;
|
||||
import ghidra.feature.vt.gui.plugin.*;
|
||||
import ghidra.feature.vt.gui.task.*;
|
||||
import ghidra.feature.vt.gui.util.VTOptionDefines;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.*;
|
||||
|
||||
public class VTMatchRemoveTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private VTController controller;
|
||||
private VTPlugin plugin;
|
||||
private VTSessionDB session;
|
||||
private ProgramDB srcProgram;
|
||||
private ProgramDB destProgram;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
env = new TestEnv();
|
||||
|
||||
ClassicSampleX86ProgramBuilder sourceBuilder = new ClassicSampleX86ProgramBuilder();
|
||||
srcProgram = sourceBuilder.getProgram();
|
||||
|
||||
ClassicSampleX86ProgramBuilder destinationBuilder = new ClassicSampleX86ProgramBuilder();
|
||||
destProgram = destinationBuilder.getProgram();
|
||||
|
||||
tool = env.getTool();
|
||||
tool.addPlugin(VTPlugin.class.getName());
|
||||
plugin = getPlugin(tool, VTPlugin.class);
|
||||
controller = new VTControllerImpl(plugin);
|
||||
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
srcProgram, destProgram, this);
|
||||
|
||||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMatch_UnaccpetedMatch() throws Exception {
|
||||
|
||||
Address srcAddr = addr("0x0100808c", srcProgram);
|
||||
Address destAddr = addr("0x0100808c", destProgram);
|
||||
|
||||
setDataOnPrograms(srcAddr, destAddr);
|
||||
String labelName = "Bob";
|
||||
addLabel(labelName, srcAddr, srcProgram);
|
||||
|
||||
VTMatch match = createMatchSetWithOneDataMatch(session, srcAddr, destAddr);
|
||||
|
||||
VTMatchSet matchSet = match.getMatchSet();
|
||||
remove(match, false);
|
||||
assertMatchRemoved(matchSet, srcAddr, destAddr);
|
||||
assertNoLabelApplied(labelName, destAddr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMatch_AccpetedMatch() throws Exception {
|
||||
|
||||
/*
|
||||
Test:
|
||||
- create and apply a match
|
||||
- remove the match
|
||||
- leave the applied markup after match removal
|
||||
*/
|
||||
|
||||
Address srcAddr = addr("0x0100808c", srcProgram);
|
||||
Address destAddr = addr("0x0100808c", destProgram);
|
||||
|
||||
setDataOnPrograms(srcAddr, destAddr);
|
||||
String labelName = "Bob";
|
||||
addLabel(labelName, srcAddr, srcProgram);
|
||||
|
||||
VTMatch match = createMatchSetWithOneDataMatch(session, srcAddr, destAddr);
|
||||
setApplyDataLabelOnAccept();
|
||||
accept(match);
|
||||
assertAcceptedAndLabelApplied(match, labelName, destAddr);
|
||||
|
||||
VTMatchSet matchSet = match.getMatchSet();
|
||||
remove(match);
|
||||
assertMatchRemoved(matchSet, srcAddr, destAddr);
|
||||
assertLabelApplied(labelName, destAddr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMatch_Accepted_MultipleMatchesForAssociation() throws Exception {
|
||||
|
||||
/*
|
||||
Test:
|
||||
- create multiple matches for the same association
|
||||
- apply one match
|
||||
- remove the applied match
|
||||
- leave the applied markup after match removal
|
||||
|
||||
*This tests control flow that avoids execution when the match being removed is the last
|
||||
match for an association.
|
||||
|
||||
*/
|
||||
|
||||
Address srcAddr = addr("0x0100808c", srcProgram);
|
||||
Address destAddr = addr("0x0100808c", destProgram);
|
||||
|
||||
setDataOnPrograms(srcAddr, destAddr);
|
||||
String labelName = "Bob";
|
||||
addLabel(labelName, srcAddr, srcProgram);
|
||||
|
||||
VTMatch match = createMatchSetWithMultipleMatchesToSameAssociation(srcAddr, destAddr);
|
||||
setApplyDataLabelOnAccept();
|
||||
accept(match);
|
||||
assertAcceptedAndLabelApplied(match, labelName, destAddr);
|
||||
|
||||
VTMatchSet matchSet = match.getMatchSet();
|
||||
remove(match, false);
|
||||
assertMatchRemoved(matchSet, srcAddr, destAddr);
|
||||
assertLabelApplied(labelName, destAddr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMatch_RejectedMatch() throws Exception {
|
||||
|
||||
Address srcAddr = addr("0x0100808c", srcProgram);
|
||||
Address destAddr = addr("0x0100808c", destProgram);
|
||||
|
||||
setDataOnPrograms(srcAddr, destAddr);
|
||||
String labelName = "Bob";
|
||||
addLabel(labelName, srcAddr, srcProgram);
|
||||
|
||||
VTMatch match = createMatchSetWithOneDataMatch(session, srcAddr, destAddr);
|
||||
setApplyDataLabelOnAccept();
|
||||
reject(match);
|
||||
assertNoLabelApplied(labelName, destAddr);
|
||||
|
||||
VTMatchSet matchSet = match.getMatchSet();
|
||||
remove(match, false);
|
||||
assertMatchRemoved(matchSet, srcAddr, destAddr);
|
||||
assertNoLabelApplied(labelName, destAddr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMatch_AccpetedMatch_ChooseNotToDelete() throws Exception {
|
||||
|
||||
/*
|
||||
Test:
|
||||
- create and apply a match
|
||||
- remove the match, but cancel at dialog prompt
|
||||
- match should still be valid; markup should still be applied
|
||||
*/
|
||||
|
||||
Address srcAddr = addr("0x0100808c", srcProgram);
|
||||
Address destAddr = addr("0x0100808c", destProgram);
|
||||
|
||||
setDataOnPrograms(srcAddr, destAddr);
|
||||
String labelName = "Bob";
|
||||
addLabel(labelName, srcAddr, srcProgram);
|
||||
|
||||
VTMatch match = createMatchSetWithOneDataMatch(session, srcAddr, destAddr);
|
||||
setApplyDataLabelOnAccept();
|
||||
accept(match);
|
||||
assertAcceptedAndLabelApplied(match, labelName, destAddr);
|
||||
|
||||
VTMatchSet matchSet = match.getMatchSet();
|
||||
startRemoveThenCancel(match);
|
||||
assertMatchNotRemoved(matchSet, srcAddr, destAddr);
|
||||
assertLabelApplied(labelName, destAddr);
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Private Methods
|
||||
//=================================================================================================
|
||||
|
||||
private void remove(VTMatch match) {
|
||||
remove(match, true);
|
||||
}
|
||||
|
||||
private void remove(VTMatch match, boolean expectPrompt) {
|
||||
RemoveMatchTask task = new RemoveMatchTask(session, List.of(match));
|
||||
|
||||
AtomicBoolean finished = runTaskLater(task); // this task is blocking, so run later and wait
|
||||
|
||||
if (expectPrompt) {
|
||||
DialogComponentProvider removeDialog =
|
||||
waitForDialogComponent("Delete ACCEPTED Matches?");
|
||||
pressButtonByText(removeDialog, "Delete Accepted Matches");
|
||||
}
|
||||
|
||||
// let the task finish processing after pressing the button
|
||||
waitFor(finished);
|
||||
waitForProgram(destProgram);
|
||||
}
|
||||
|
||||
private void startRemoveThenCancel(VTMatch match) {
|
||||
RemoveMatchTask task = new RemoveMatchTask(session, List.of(match));
|
||||
|
||||
AtomicBoolean finished = runTaskLater(task); // this task is blocking, so run later and wait
|
||||
|
||||
DialogComponentProvider removeDialog = waitForDialogComponent("Delete ACCEPTED Matches?");
|
||||
pressButtonByText(removeDialog, "Finish");
|
||||
|
||||
// let the task finish processing after pressing the button
|
||||
waitFor(finished);
|
||||
waitForProgram(destProgram);
|
||||
}
|
||||
|
||||
private void assertLabelApplied(String labelName, Address addr) {
|
||||
assertEquals(labelName, getSymbol(destProgram, addr).getName());
|
||||
}
|
||||
|
||||
private void assertNoLabelApplied(String labelName, Address addr) {
|
||||
Symbol symbol = getSymbol(destProgram, addr);
|
||||
if (symbol == null) {
|
||||
return; // no label; expected
|
||||
}
|
||||
assertNotEquals(labelName, symbol.getName());
|
||||
}
|
||||
|
||||
private void assertMatchRemoved(VTMatchSet matchSet, Address srcAddr, Address destAddr) {
|
||||
Collection<VTMatch> matches = matchSet.getMatches(srcAddr, destAddr);
|
||||
assertTrue(matches.isEmpty());
|
||||
}
|
||||
|
||||
private void assertMatchNotRemoved(VTMatchSet matchSet, Address srcAddr, Address destAddr) {
|
||||
Collection<VTMatch> matches = matchSet.getMatches(srcAddr, destAddr);
|
||||
assertFalse(matches.isEmpty());
|
||||
}
|
||||
|
||||
private void assertAcceptedAndLabelApplied(VTMatch match, String labelName, Address addr) {
|
||||
VTAssociationStatus status = match.getAssociation().getStatus();
|
||||
assertEquals(VTAssociationStatus.ACCEPTED, status);
|
||||
assertEquals(labelName, getSymbol(destProgram, addr).getName());
|
||||
}
|
||||
|
||||
private Symbol getSymbol(Program p, Address addr) {
|
||||
return p.getSymbolTable().getPrimarySymbol(addr);
|
||||
}
|
||||
|
||||
private void setApplyDataLabelOnAccept() {
|
||||
ToolOptions options = controller.getOptions();
|
||||
options.setBoolean(VTOptionDefines.APPLY_DATA_NAME_ON_ACCEPT, true);
|
||||
}
|
||||
|
||||
private void accept(VTMatch match) throws Exception {
|
||||
AcceptMatchTask task = new AcceptMatchTask(controller, List.of(match));
|
||||
runTask(task);
|
||||
}
|
||||
|
||||
private void reject(VTMatch match) throws Exception {
|
||||
RejectMatchTask task = new RejectMatchTask(session, List.of(match));
|
||||
runTask(task);
|
||||
}
|
||||
|
||||
private void setDataOnPrograms(Address srcAddr, Address destAddr) {
|
||||
DataType srcDt = new DWordDataType();
|
||||
DataType destDt1 = new StringDataType();
|
||||
DataType destDt2 = new WordDataType();
|
||||
setData(srcDt, 4, srcAddr, srcProgram);
|
||||
setData(destDt1, 2, destAddr, destProgram);
|
||||
setData(destDt2, 2, destAddr.add(2), destProgram);
|
||||
}
|
||||
|
||||
private Symbol addLabel(String name, Address address, Program program) {
|
||||
return tx(program, () -> {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
return symbolTable.createLabel(address, name, SourceType.USER_DEFINED);
|
||||
});
|
||||
}
|
||||
|
||||
private Data setData(DataType dataType, int length, Address address, Program program) {
|
||||
return tx(program, () -> {
|
||||
Listing listing = program.getListing();
|
||||
return listing.createData(address, dataType, length);
|
||||
});
|
||||
}
|
||||
|
||||
private AtomicBoolean runTaskLater(VtTask task) {
|
||||
AtomicBoolean finishedFlag = new AtomicBoolean();
|
||||
runSwingLater(() -> {
|
||||
controller.runVTTask(task);
|
||||
finishedFlag.set(true);
|
||||
});
|
||||
waitForSwing();
|
||||
return finishedFlag;
|
||||
}
|
||||
|
||||
private void runTask(VtTask task) {
|
||||
controller.runVTTask(task);
|
||||
waitForProgram(destProgram);
|
||||
}
|
||||
|
||||
private VTMatch createMatchSetWithMultipleMatchesToSameAssociation(Address srcAddr,
|
||||
Address destAddr) throws Exception {
|
||||
int txId = 0;
|
||||
try {
|
||||
txId = session.startTransaction("Test Create Data Match Set");
|
||||
VTMatchInfo info = createRandomMatch(srcAddr, destAddr, session);
|
||||
info.setAssociationType(VTAssociationType.DATA);
|
||||
VTMatchSet matchSet =
|
||||
session.createMatchSet(createProgramCorrelator(srcProgram, destProgram));
|
||||
VTMatch firstMatch = matchSet.addMatch(info);
|
||||
|
||||
// create a second match, match set and correlator, all tied to the given association
|
||||
DummyTestProgramCorrelator pc2 =
|
||||
(DummyTestProgramCorrelator) createProgramCorrelator(srcProgram, destProgram);
|
||||
pc2.setName("Correlator Two");
|
||||
VTMatchSet ms2 = session.createMatchSet(pc2);
|
||||
ms2.addMatch(createRandomMatch(srcAddr, destAddr, session));
|
||||
|
||||
return firstMatch;
|
||||
}
|
||||
finally {
|
||||
session.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
public class DummyTestProgramCorrelator extends VTAbstractProgramCorrelator {
|
||||
|
||||
private String name = "DummyTestProgramCorrelator";
|
||||
private int matchCount = 1;
|
||||
|
||||
public DummyTestProgramCorrelator() {
|
||||
|
@ -84,8 +85,12 @@ public class DummyTestProgramCorrelator extends VTAbstractProgramCorrelator {
|
|||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "DummyTestProgramCorrelator";
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ public class VTDomainObjectEventsTest extends VTBaseTestCase {
|
|||
assertEquals(VTEvent.MATCH_ADDED, events.get(0).getEventType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal") // ignore the warning until removeMatch() is removed
|
||||
@Test
|
||||
public void testEventsForRemovingLastMatchForAssociation() {
|
||||
VTMatchSet manualMatchSet = db.getManualMatchSet();
|
||||
|
@ -134,6 +135,22 @@ public class VTDomainObjectEventsTest extends VTBaseTestCase {
|
|||
assertEquals(VTEvent.MATCH_DELETED, events.get(1).getEventType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventsForDeletingLastMatchForAssociation() {
|
||||
VTMatchSet manualMatchSet = db.getManualMatchSet();
|
||||
clearEvents();
|
||||
VTMatchInfo matchInfo = VTTestUtils.createRandomMatch(null);
|
||||
VTMatch match = manualMatchSet.addMatch(matchInfo);
|
||||
clearEvents();
|
||||
|
||||
manualMatchSet.deleteMatch(match);
|
||||
|
||||
assertEventCount(2);
|
||||
assertEquals(VTEvent.ASSOCIATION_REMOVED, events.get(0).getEventType());
|
||||
assertEquals(VTEvent.MATCH_DELETED, events.get(1).getEventType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal") // ignore the warning until removeMatch() is removed
|
||||
@Test
|
||||
public void testEventsForRemovingNonLastMatchForAssociation() {
|
||||
VTMatchSet manualMatchSet = db.getManualMatchSet();
|
||||
|
@ -149,6 +166,21 @@ public class VTDomainObjectEventsTest extends VTBaseTestCase {
|
|||
assertEquals(VTEvent.MATCH_DELETED, events.get(1).getEventType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventsForDeletingNonLastMatchForAssociation() {
|
||||
VTMatchSet manualMatchSet = db.getManualMatchSet();
|
||||
clearEvents();
|
||||
VTMatchInfo matchInfo = VTTestUtils.createRandomMatch(null);
|
||||
VTMatch match = manualMatchSet.addMatch(matchInfo);
|
||||
clearEvents();
|
||||
|
||||
manualMatchSet.deleteMatch(match);
|
||||
|
||||
assertEventCount(2);
|
||||
assertEquals(VTEvent.ASSOCIATION_REMOVED, events.get(0).getEventType());
|
||||
assertEquals(VTEvent.MATCH_DELETED, events.get(1).getEventType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventsForRejectingMatch() throws VTAssociationStatusException {
|
||||
VTMatchSet matchSet = createMatchSet();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue