mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge branch 'GP-266-dragonmacher-function-tags-ui-lockup'
This commit is contained in:
commit
8ec3f786ff
21 changed files with 712 additions and 803 deletions
|
@ -34,25 +34,20 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<UL>
|
||||
<LI><B>All Tags TAble</B>: Displays all tags that are available to assign to the
|
||||
<LI><B>All Tags</B>: Displays all tags that are available to assign to the
|
||||
current function, as well as a count of the number of times each tag has been used
|
||||
in the current program.</LI>
|
||||
|
||||
<LI><B>Assigned Tags List</B>: Displays all tags that have been assigned to the current
|
||||
<LI><B>Assigned Tags</B>: Displays all tags that have been assigned to the current
|
||||
function.</LI>
|
||||
|
||||
<LI><B>Function Panel</B>: Displays functions that contain any selected tags. If multiple
|
||||
<LI><B>Functions</B>: Displays functions that contain any selected tags. If multiple
|
||||
tags are selected, functions containing ANY of the tags will be displayed.</LI>
|
||||
|
||||
<LI>
|
||||
<B>Tag Input Field</B>: Allows users to create new tags. Multiple tags may be created at one time.
|
||||
<P style="margin: 20px;"><IMG alt="" border="0" src="images/InputField.png"></P>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
<B>Filter Field</B>: Allows users to filter what is shown in the Available and Assigned tag lists.
|
||||
<P style="margin: 20px;"><IMG alt="" border="0" src="images/FilterField.png"></P>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
<UL class="noindent">
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
|
@ -16,23 +16,16 @@
|
|||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.table.GTableFilterPanel;
|
||||
import docking.widgets.table.columnfilter.ColumnBasedTableFilter;
|
||||
import docking.widgets.table.columnfilter.LogicOperation;
|
||||
import docking.widgets.table.constraint.*;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.field.AddressBasedLocation;
|
||||
import ghidra.util.table.*;
|
||||
|
||||
/**
|
||||
* Displays all functions that are associated with the selected tag in the
|
||||
|
@ -41,10 +34,9 @@ import ghidra.util.table.field.AddressBasedLocation;
|
|||
public class AllFunctionsPanel extends JPanel {
|
||||
|
||||
private FunctionTableModel model;
|
||||
private FunctionTableModel filteredModel;
|
||||
private JLabel titleLabel;
|
||||
private String filterText;
|
||||
private GhidraTable table;
|
||||
private GhidraTableFilterPanel<Function> filterPanel;
|
||||
private JLabel titleLabel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -56,16 +48,19 @@ public class AllFunctionsPanel extends JPanel {
|
|||
public AllFunctionsPanel(Program program, ComponentProviderAdapter provider, String title) {
|
||||
|
||||
model = new FunctionTableModel(title, provider.getTool(), program, null);
|
||||
filteredModel = new FunctionTableModel(title, provider.getTool(), program, null);
|
||||
GhidraThreadedTablePanel<Function> tablePanel =
|
||||
new GhidraThreadedTablePanel<>(model);
|
||||
|
||||
table = new GhidraTable(filteredModel);
|
||||
table = tablePanel.getTable();
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, model);
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
titleLabel = new JLabel(title);
|
||||
titleLabel.setBorder(BorderFactory.createEmptyBorder(3, 5, 0, 0));
|
||||
|
||||
add(titleLabel, BorderLayout.NORTH);
|
||||
add(new JScrollPane(table), BorderLayout.CENTER);
|
||||
add(tablePanel, BorderLayout.CENTER);
|
||||
add(filterPanel, BorderLayout.SOUTH);
|
||||
|
||||
GoToService goToService = provider.getTool().getService(GoToService.class);
|
||||
if (goToService != null) {
|
||||
|
@ -73,26 +68,11 @@ public class AllFunctionsPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the table based on the given text
|
||||
* <p>
|
||||
* Note that this panel shares a filter with the other panels in the
|
||||
* function tag dialog, so it doesn't use a dedicated
|
||||
* {@link GTableFilterPanel}.
|
||||
*
|
||||
* @param filterString the filter text
|
||||
*/
|
||||
public void setFilterText(String filter) {
|
||||
this.filterText = filter;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the table with whatever is in the {@link #model}
|
||||
*/
|
||||
public void refresh() {
|
||||
applyFilter();
|
||||
filteredModel.refresh();
|
||||
model.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +80,7 @@ public class AllFunctionsPanel extends JPanel {
|
|||
*
|
||||
* @param selectedTags the selected function tags
|
||||
*/
|
||||
public void refresh(List<FunctionTag> selectedTags) {
|
||||
public void refresh(Set<FunctionTag> selectedTags) {
|
||||
setSelectedTags(selectedTags);
|
||||
}
|
||||
|
||||
|
@ -111,7 +91,6 @@ public class AllFunctionsPanel extends JPanel {
|
|||
*/
|
||||
public void setProgram(Program program) {
|
||||
model.setProgram(program);
|
||||
filteredModel.setProgram(program);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,7 +99,7 @@ public class AllFunctionsPanel extends JPanel {
|
|||
*
|
||||
* @param tags the selected tags
|
||||
*/
|
||||
public void setSelectedTags(List<FunctionTag> tags) {
|
||||
public void setSelectedTags(Set<FunctionTag> tags) {
|
||||
String tagNames = tags.stream()
|
||||
.map(t -> t.getName())
|
||||
.collect(Collectors.joining(" or "))
|
||||
|
@ -128,8 +107,6 @@ public class AllFunctionsPanel extends JPanel {
|
|||
|
||||
titleLabel.setText("Functions With Tag: " + tagNames);
|
||||
model.setSelectedTags(tags);
|
||||
filteredModel.setSelectedTags(tags);
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,51 +128,4 @@ public class AllFunctionsPanel extends JPanel {
|
|||
public FunctionTableModel getTableModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
if (StringUtils.isEmpty(filterText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ColumnBasedTableFilter<Function> tableFilter = new ColumnBasedTableFilter<>(filteredModel);
|
||||
tableFilter.addConstraintSet(LogicOperation.OR, 0,
|
||||
Arrays.asList(new StringContainsColumnConstraint(filterText)));
|
||||
tableFilter.addConstraintSet(LogicOperation.OR, 1, Arrays.asList(
|
||||
new LocationContainsColumnConstraint(new StringContainsColumnConstraint(filterText))));
|
||||
tableFilter.addConstraintSet(LogicOperation.OR, 2,
|
||||
Arrays.asList(new StringContainsColumnConstraint(filterText)));
|
||||
|
||||
filteredModel.setTableFilter(tableFilter);
|
||||
filteredModel.reFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link AddressBasedLocation} to a string
|
||||
*/
|
||||
private class LocationToStringMapper
|
||||
extends ColumnTypeMapper<AddressBasedLocation, String> {
|
||||
|
||||
@Override
|
||||
public String convert(AddressBasedLocation value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Column constraint stipulating that a given {@link AddressBasedLocation}
|
||||
* contains a specified string
|
||||
*/
|
||||
private class LocationContainsColumnConstraint
|
||||
extends MappedColumnConstraint<AddressBasedLocation, String> {
|
||||
|
||||
public LocationContainsColumnConstraint(ColumnConstraint<String> delegate) {
|
||||
super(new LocationToStringMapper(), delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnConstraint<AddressBasedLocation> copy(ColumnConstraint<String> value) {
|
||||
return new LocationContainsColumnConstraint(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.widgets.table.DiscoverableTableUtils;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
|
@ -37,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
class FunctionTableModel extends AddressBasedTableModel<Function> {
|
||||
|
||||
// The function tags to display functions for
|
||||
private List<FunctionTag> tags;
|
||||
private Set<FunctionTag> tags;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -104,7 +105,7 @@ class FunctionTableModel extends AddressBasedTableModel<Function> {
|
|||
*
|
||||
* @param tags the selected tags
|
||||
*/
|
||||
public void setSelectedTags(List<FunctionTag> tags) {
|
||||
public void setSelectedTags(Set<FunctionTag> tags) {
|
||||
this.tags = tags;
|
||||
reload();
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.xml.sax.*;
|
||||
|
||||
|
@ -29,17 +29,20 @@ import ghidra.xml.*;
|
|||
|
||||
/**
|
||||
* Reads function tags from @see ghidra.framework.Application#getModuleDataFile(java.lang.String)
|
||||
* or a File on the filesytem.
|
||||
* or a File on the filesystem.
|
||||
*/
|
||||
public class FunctionTagLoader {
|
||||
|
||||
// TODO this class should provide a system property to allow users to load files defined in
|
||||
// the property
|
||||
|
||||
/**
|
||||
* Load function tags from filesystem. Useful for unit tests.
|
||||
*
|
||||
* @param tagFile tag file
|
||||
* @return List list of function tags
|
||||
*/
|
||||
protected static List<FunctionTag> loadTags(File tagFile) {
|
||||
protected static Set<FunctionTag> loadTags(File tagFile) {
|
||||
return loadTags(new ResourceFile(tagFile));
|
||||
}
|
||||
|
||||
|
@ -49,18 +52,19 @@ public class FunctionTagLoader {
|
|||
* @param moduleDataFilePath data file loaded by Application
|
||||
* @return List list of function tags
|
||||
*/
|
||||
protected static List<FunctionTag> loadTags(String moduleDataFilePath) {
|
||||
protected static Set<FunctionTag> loadTags(String moduleDataFilePath) {
|
||||
try {
|
||||
return loadTags(Application.getModuleDataFile(moduleDataFilePath));
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Msg.error(null, "Error loading function tags file from " + moduleDataFilePath, e);
|
||||
Msg.error(FunctionTagLoader.class,
|
||||
"Error loading function tags file from " + moduleDataFilePath, e);
|
||||
}
|
||||
return new ArrayList<FunctionTag>();
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
protected static List<FunctionTag> loadTags(final ResourceFile tagDataFile) {
|
||||
List<FunctionTag> tags = new ArrayList<>();
|
||||
protected static Set<FunctionTag> loadTags(final ResourceFile tagDataFile) {
|
||||
Set<FunctionTag> tags = new HashSet<>();
|
||||
|
||||
try {
|
||||
ErrorHandler errHandler = new ErrorHandler() {
|
||||
|
@ -101,7 +105,7 @@ public class FunctionTagLoader {
|
|||
el = parser.next();
|
||||
comment = parser.end().getText();
|
||||
}
|
||||
FunctionTagTemp tag = new FunctionTagTemp(name, comment);
|
||||
InMemoryFunctionTag tag = new InMemoryFunctionTag(name, comment);
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
|
@ -109,10 +113,12 @@ public class FunctionTagLoader {
|
|||
parser.dispose();
|
||||
}
|
||||
catch (XmlException e) {
|
||||
Msg.error(null, "Error parsing function tags from " + tagDataFile, e);
|
||||
Msg.error(FunctionTagLoader.class, "Error parsing function tags from " + tagDataFile,
|
||||
e);
|
||||
}
|
||||
catch (SAXException | IOException e) {
|
||||
Msg.error(null, "Error loading function tags from " + tagDataFile, e);
|
||||
Msg.error(FunctionTagLoader.class, "Error loading function tags from " + tagDataFile,
|
||||
e);
|
||||
}
|
||||
|
||||
return tags;
|
||||
|
|
|
@ -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.plugin.core.function.tags;
|
||||
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
|
||||
class FunctionTagRowObject {
|
||||
|
||||
private FunctionTag tag;
|
||||
private int count;
|
||||
|
||||
FunctionTagRowObject(FunctionTag tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
FunctionTag getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return tag.getName();
|
||||
}
|
||||
|
||||
int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
boolean isImmutable() {
|
||||
return tag instanceof InMemoryFunctionTag;
|
||||
}
|
||||
|
||||
String getComment() {
|
||||
return tag.getComment();
|
||||
}
|
||||
}
|
|
@ -21,13 +21,14 @@ import java.awt.event.MouseEvent;
|
|||
import java.util.Set;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderingData;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.GhidraTableCellRenderer;
|
||||
|
||||
/**
|
||||
* Table that displays function tags and a count of the number of times
|
||||
|
@ -44,6 +45,8 @@ public class FunctionTagTable extends GhidraTable {
|
|||
/** The selected function */
|
||||
private Function function = null;
|
||||
|
||||
private TagRenderer renderer = new TagRenderer();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
@ -65,16 +68,14 @@ public class FunctionTagTable extends GhidraTable {
|
|||
public String getToolTipText(MouseEvent evt) {
|
||||
FunctionTagTable table = (FunctionTagTable) evt.getSource();
|
||||
int row = this.rowAtPoint(evt.getPoint());
|
||||
int nameCol = table.getColumnModel().getColumnIndex("Name");
|
||||
String tagName = (String)table.getValueAt(row, nameCol);
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
FunctionTag tag = model.getTag(tagName);
|
||||
|
||||
if (tag.getComment().isEmpty()) {
|
||||
FunctionTagRowObject rowObject = model.getRowObject(row);
|
||||
String comment = rowObject.getComment();
|
||||
if (comment.isEmpty()) {
|
||||
return "no tooltip set";
|
||||
}
|
||||
|
||||
return "<html>" + HTMLUtilities.escapeHTML(tag.getComment());
|
||||
return "<html>" + HTMLUtilities.escapeHTML(comment);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,42 +85,40 @@ public class FunctionTagTable extends GhidraTable {
|
|||
*/
|
||||
@Override
|
||||
public TableCellRenderer getCellRenderer(int row, int col) {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
return new TableCellRenderer() {
|
||||
private class TagRenderer extends GhidraTableCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
Component c = super.getTableCellRendererComponent(data);
|
||||
|
||||
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
|
||||
Component c = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
JTable table = data.getTable();
|
||||
int nameColumn = table.getColumnModel().getColumnIndex("Name");
|
||||
|
||||
int row = data.getRowViewIndex();
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
FunctionTagRowObject rowObject = model.getRowObject(row);
|
||||
|
||||
boolean enableRow = true;
|
||||
if (disable && function != null) {
|
||||
int nameCol = table.getColumnModel().getColumnIndex("Name");
|
||||
String nameVal = (String)table.getValueAt(row, nameCol);
|
||||
String tagName = rowObject.getName();
|
||||
Set<FunctionTag> tags = function.getTags();
|
||||
enableRow = !tags.stream().anyMatch(t -> t.getName().equals(nameVal));
|
||||
enableRow = !tags.stream().anyMatch(t -> t.getName().equals(tagName));
|
||||
}
|
||||
c.setEnabled(enableRow);
|
||||
|
||||
switch (table.getColumnName(column)) {
|
||||
case "Count":
|
||||
break;
|
||||
case "Name":
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
FunctionTag tag = model.getTag((String)value);
|
||||
if (tag instanceof FunctionTagTemp) {
|
||||
c.setFont(getFont().deriveFont(Font.PLAIN));
|
||||
|
||||
int column = data.getColumnViewIndex();
|
||||
if (column == nameColumn) {
|
||||
if (rowObject.isImmutable()) {
|
||||
c.setFont(getFont().deriveFont(Font.ITALIC));
|
||||
}
|
||||
else {
|
||||
c.setFont(getFont().deriveFont(Font.PLAIN));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,40 +15,34 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import docking.widgets.table.AbstractDynamicTableColumnStub;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.Counter;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Model that backs a {@link FunctionTagTable}
|
||||
*/
|
||||
public class FunctionTagTableModel extends ThreadedTableModel<FunctionTag, List<FunctionTag>> {
|
||||
public class FunctionTagTableModel extends ThreadedTableModel<FunctionTagRowObject, Program> {
|
||||
|
||||
/** The list of tags to display in the table */
|
||||
private List<FunctionTag> tags = new ArrayList<>();
|
||||
private Program program;
|
||||
private Supplier<Set<FunctionTag>> tagLoader;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param modelName the name of this table model
|
||||
* @param serviceProvider the service provider
|
||||
*/
|
||||
protected FunctionTagTableModel(String modelName, ServiceProvider serviceProvider) {
|
||||
protected FunctionTagTableModel(String modelName, ServiceProvider serviceProvider,
|
||||
Supplier<Set<FunctionTag>> tagLoader) {
|
||||
super(modelName, serviceProvider);
|
||||
this.tagLoader = tagLoader;
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
|
@ -56,15 +50,49 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTag, List<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(Accumulator<FunctionTag> accumulator, TaskMonitor monitor)
|
||||
protected void doLoad(Accumulator<FunctionTagRowObject> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
accumulator.addAll(tags);
|
||||
fireTableDataChanged();
|
||||
|
||||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Counter> countsByName = getFunctionTagCountsByName(monitor);
|
||||
for (FunctionTag tag : tagLoader.get()) {
|
||||
|
||||
FunctionTagRowObject rowObject = new FunctionTagRowObject(tag);
|
||||
Counter counter = countsByName.get(tag.getName());
|
||||
rowObject.setCount(counter.count);
|
||||
accumulator.add(rowObject);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Counter> getFunctionTagCountsByName(TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
monitor.initialize(functionManager.getFunctionCount());
|
||||
|
||||
Map<String, Counter> map = LazyMap.lazyMap(new HashMap<>(), () -> new Counter());
|
||||
FunctionIterator it = functionManager.getFunctions(true);
|
||||
for (Function function : it) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Set<FunctionTag> functionTags = function.getTags();
|
||||
for (FunctionTag tag : functionTags) {
|
||||
|
||||
String name = tag.getName();
|
||||
map.get(name).count++;
|
||||
}
|
||||
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<FunctionTag> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<FunctionTag> descriptor = new TableColumnDescriptor<>();
|
||||
protected TableColumnDescriptor<FunctionTagRowObject> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<FunctionTagRowObject> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(new FunctionTagNameColumn());
|
||||
descriptor.addVisibleColumn(new FunctionTagCountColumn());
|
||||
|
@ -73,85 +101,39 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTag, List<
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<FunctionTag> getDataSource() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function tag to the table. If a tag with the same name is already
|
||||
* present in the table, does nothing.
|
||||
*
|
||||
* @param tag the function tag to add
|
||||
*/
|
||||
public void addTag(FunctionTag tag) {
|
||||
Optional<FunctionTag> existingTag = tags.stream()
|
||||
.filter(t -> t.getName().equals(tag.getName()))
|
||||
.findAny();
|
||||
if (existingTag.isPresent()) {
|
||||
tags.remove(existingTag.get());
|
||||
}
|
||||
|
||||
tags.add(tag);
|
||||
fireTableDataChanged();
|
||||
public Program getDataSource() {
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all function tags from the model
|
||||
*/
|
||||
public void clear() {
|
||||
tags.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all function tags in the model
|
||||
*
|
||||
* @return all function tags
|
||||
*/
|
||||
public List<FunctionTag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FunctionTag} object with a given name
|
||||
*
|
||||
* @param name the tag name to search for
|
||||
* @return the function tag
|
||||
*/
|
||||
public FunctionTag getTag(String name) {
|
||||
Optional<FunctionTag> tag = tags.stream()
|
||||
.filter(t -> t.getName().equals(name))
|
||||
.findAny();
|
||||
if (tag.isPresent()) {
|
||||
return tag.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
super.clearData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a function tag with a given name is in the model
|
||||
*
|
||||
* @param name the tag name to search fo
|
||||
* @param name the tag name to search for
|
||||
* @return true if the tag exists in the model
|
||||
*/
|
||||
public boolean isTagInModel(String name) {
|
||||
Optional<FunctionTag> tag = tags.stream()
|
||||
.filter(t -> t.getName().equals(name))
|
||||
.findAny();
|
||||
return tag.isPresent();
|
||||
public boolean containsTag(String name) {
|
||||
return getModelData().stream().anyMatch(row -> row.getName().equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Table column that displays a count of the number of times a function tag has been
|
||||
* applied to a function (in the selected program)
|
||||
*/
|
||||
private class FunctionTagCountColumn extends AbstractDynamicTableColumnStub<FunctionTag, Integer> {
|
||||
private class FunctionTagCountColumn
|
||||
extends AbstractDynamicTableColumnStub<FunctionTagRowObject, Integer> {
|
||||
|
||||
@Override
|
||||
public String getColumnDisplayName(Settings settings) {
|
||||
return " "; // don't display any name, but need it to be at least one space
|
||||
// wide so the correct space is allocated to the header
|
||||
// don't display any name, but need it to be at least one space wide so the correct
|
||||
// space is allocated to the header
|
||||
return " ";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,33 +147,17 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTag, List<
|
|||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue(FunctionTag rowObject, Settings settings,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
int count = 0;
|
||||
|
||||
if (program == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionIterator iter = program.getFunctionManager().getFunctions(true);
|
||||
while (iter.hasNext()) {
|
||||
Function f = iter.next();
|
||||
Optional<FunctionTag> foundTag = f.getTags()
|
||||
.stream()
|
||||
.filter(t -> t.getName().equals(rowObject.getName()))
|
||||
.findAny();
|
||||
if (foundTag.isPresent()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
public Integer getValue(FunctionTagRowObject rowObject, Settings settings,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
return rowObject.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Table column that displays the name of a function tag
|
||||
*/
|
||||
private class FunctionTagNameColumn extends AbstractDynamicTableColumnStub<FunctionTag, String> {
|
||||
private class FunctionTagNameColumn
|
||||
extends AbstractDynamicTableColumnStub<FunctionTagRowObject, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
|
@ -199,8 +165,8 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTag, List<
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getValue(FunctionTag rowObject, Settings settings,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
public String getValue(FunctionTagRowObject rowObject, Settings settings,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
return rowObject.getName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,9 @@
|
|||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
@ -65,9 +64,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
private JPanel mainPanel;
|
||||
|
||||
private JPanel inputPanel;
|
||||
private JPanel filterPanel;
|
||||
private HintTextField tagInputTF;
|
||||
private HintTextField filterInputTF;
|
||||
private HintTextField tagInputField;
|
||||
|
||||
private int MIN_WIDTH = 850;
|
||||
private int MIN_HEIGHT = 350;
|
||||
|
@ -104,12 +101,8 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
*/
|
||||
public void reload() {
|
||||
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
|
||||
if (tagInputTF != null) {
|
||||
tagInputTF.setText("");
|
||||
}
|
||||
|
||||
Swing.runLater(() -> {
|
||||
tagInputField.setText("");
|
||||
updateTitle(currentLocation);
|
||||
updateTagLists();
|
||||
});
|
||||
|
@ -127,6 +120,10 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
return mainPanel;
|
||||
}
|
||||
|
||||
HintTextField getTagInputField() {
|
||||
return tagInputField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a new location has been detected in the listing. When
|
||||
* this happens we need to update the tag list to show what tags are assigned
|
||||
|
@ -197,7 +194,6 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
JPanel bottomPanel = new JPanel();
|
||||
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
|
||||
bottomPanel.add(createInputPanel());
|
||||
bottomPanel.add(createFilterPanel());
|
||||
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
mainPanel.setPreferredSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
|
||||
|
@ -254,12 +250,20 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
sourcePanel.clearSelection();
|
||||
}
|
||||
|
||||
List<FunctionTag> sourceTags = sourcePanel.getSelectedTags();
|
||||
List<FunctionTag> targetTags = targetPanel.getSelectedTags();
|
||||
Set<FunctionTag> sourceTags = sourcePanel.getSelectedTags();
|
||||
Set<FunctionTag> targetTags = targetPanel.getSelectedTags();
|
||||
sourceTags.addAll(targetTags);
|
||||
allFunctionsPanel.setSelectedTags(sourceTags);
|
||||
}
|
||||
|
||||
TargetTagsPanel getTargetPanel() {
|
||||
return targetPanel;
|
||||
}
|
||||
|
||||
SourceTagsPanel getSourcePanel() {
|
||||
return sourcePanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Function} at the given program location. If not a function, or
|
||||
* if the location is not a pointer to a function returns null.
|
||||
|
@ -319,11 +323,10 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
targetPanel.setProgram(program);
|
||||
allFunctionsPanel.setProgram(program);
|
||||
|
||||
// Get the currently selected tags and use them to update the
|
||||
// all functions panel. If there is no current selection, leave the
|
||||
// table as-is.
|
||||
List<FunctionTag> sTags = sourcePanel.getSelectedTags();
|
||||
List<FunctionTag> tTags = targetPanel.getSelectedTags();
|
||||
// Get the currently selected tags and use them to update the all functions panel. If
|
||||
// there is no current selection, leave the table as-is.
|
||||
Set<FunctionTag> sTags = sourcePanel.getSelectedTags();
|
||||
Set<FunctionTag> tTags = targetPanel.getSelectedTags();
|
||||
sTags.addAll(tTags);
|
||||
if (!sTags.isEmpty()) {
|
||||
allFunctionsPanel.refresh(sTags);
|
||||
|
@ -369,7 +372,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
private List<String> getInputNames() {
|
||||
|
||||
// First split the string on the delimiter to get all the entries.
|
||||
String[] names = tagInputTF.getText().split(INPUT_DELIMITER);
|
||||
String[] names = tagInputField.getText().split(INPUT_DELIMITER);
|
||||
|
||||
// Trim each item to remove any leading/trailing whitespace and add to
|
||||
// the return list.
|
||||
|
@ -383,41 +386,6 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
return nameList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panel that allows users to enter text that will be used
|
||||
* as a filter on the source and target lists.
|
||||
*
|
||||
* @return the new filter panel
|
||||
*/
|
||||
private JPanel createFilterPanel() {
|
||||
filterPanel = new JPanel(new BorderLayout());
|
||||
|
||||
filterInputTF = new HintTextField("");
|
||||
filterInputTF.setName("filterInputTF");
|
||||
filterInputTF.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
JTextField textField = (JTextField) e.getSource();
|
||||
String text = textField.getText();
|
||||
sourcePanel.setFilterText(text);
|
||||
targetPanel.setFilterText(text);
|
||||
allFunctionsPanel.setFilterText(text);
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
filterInputTF.setBackground(Color.YELLOW);
|
||||
}
|
||||
else {
|
||||
filterInputTF.setBackground(Color.WHITE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
filterPanel.add(new GLabel(" Tag Filter:"), BorderLayout.WEST);
|
||||
filterPanel.add(filterInputTF, BorderLayout.CENTER);
|
||||
|
||||
return filterPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the text-entry panel for adding new tag names.
|
||||
*
|
||||
|
@ -426,12 +394,12 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
|
|||
private JPanel createInputPanel() {
|
||||
|
||||
inputPanel = new JPanel(new BorderLayout());
|
||||
tagInputTF = new HintTextField("tag 1, tag 2, ...");
|
||||
tagInputTF.setName("tagInputTF");
|
||||
tagInputTF.addActionListener(e -> processCreates());
|
||||
tagInputField = new HintTextField("tag 1, tag 2, ...");
|
||||
tagInputField.setName("tagInputTF");
|
||||
tagInputField.addActionListener(e -> processCreates());
|
||||
|
||||
inputPanel.add(new GLabel(" Create new tag(s):"), BorderLayout.WEST);
|
||||
inputPanel.add(tagInputTF, BorderLayout.CENTER);
|
||||
inputPanel.add(tagInputField, BorderLayout.CENTER);
|
||||
|
||||
return inputPanel;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
|
||||
/**
|
||||
|
@ -22,23 +24,18 @@ import ghidra.program.model.listing.FunctionTag;
|
|||
* tags that are not yet ready to be inserted into the database. This was created
|
||||
* to allow tags to be imported from an external file and made available to the user
|
||||
* through the {@link FunctionTagsComponentProvider} UI without needing to formally
|
||||
* add them to the {@link FunctionTagAdapter} table.
|
||||
* add them to the {@code FunctionTagAdapter} table.
|
||||
*/
|
||||
class FunctionTagTemp implements FunctionTag {
|
||||
class InMemoryFunctionTag implements FunctionTag {
|
||||
|
||||
private final String name;
|
||||
private final String comment;
|
||||
|
||||
FunctionTagTemp(String name, String comment) {
|
||||
InMemoryFunctionTag(String name, String comment) {
|
||||
this.name = name;
|
||||
this.comment = comment;
|
||||
this.comment = comment == null ? "" : comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* These objects are not in the database, so just return a bogus id.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public long getId() {
|
||||
return -1;
|
||||
|
@ -93,23 +90,16 @@ class FunctionTagTemp implements FunctionTag {
|
|||
if (!(obj instanceof FunctionTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionTag other = (FunctionTag) obj;
|
||||
if (comment == null) {
|
||||
if (other.getComment() != null) {
|
||||
if (!Objects.equals(comment, other.getComment())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!comment.equals(other.getComment())) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.getName() != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!name.equals(other.getName())) {
|
||||
|
||||
if (!Objects.equals(name, other.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -118,4 +108,8 @@ class FunctionTagTemp implements FunctionTag {
|
|||
// These items cannot be deleted, so do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "In-memory tag: " + name;
|
||||
}
|
||||
}
|
|
@ -15,12 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.cmd.function.AddFunctionTagCmd;
|
||||
import ghidra.app.cmd.function.CreateFunctionTagCmd;
|
||||
|
@ -29,7 +24,6 @@ import ghidra.framework.plugintool.PluginTool;
|
|||
import ghidra.program.database.function.FunctionManagerDB;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
import ghidra.util.Msg;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
|
@ -54,15 +48,10 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
*/
|
||||
private static String TAG_FILE = "functionTags.xml";
|
||||
|
||||
// List of tags read in from the external file. These will be displayed in a different
|
||||
// color than 'regular' tags and cannot be edited or deleted, until they're added to a function
|
||||
private List<FunctionTag> tempTags = new ArrayList<>();
|
||||
|
||||
// Keeps a list of the original temp tags as loaded from file. This is necessary
|
||||
// when switching between programs where we need to know the original state of the
|
||||
// temporary tags. Without this we would need to reload from file on each new program
|
||||
// activation.
|
||||
private List<FunctionTag> tempTagsCache;
|
||||
// Keeps a list of the original tags as loaded from file. This is necessary when switching
|
||||
// between programs where we need to know the original state of the disabled tags. Without
|
||||
// this we would need to reload from file on each new program activation.
|
||||
private Set<FunctionTag> tagsFromFile;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -74,9 +63,8 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
public SourceTagsPanel(FunctionTagsComponentProvider provider, PluginTool tool, String title) {
|
||||
super(provider, tool, title);
|
||||
|
||||
// Load any tags from external sources and keep a copy in the cache
|
||||
tempTags = loadTags();
|
||||
tempTagsCache = new ArrayList<>(tempTags);
|
||||
// Load any tags from external sources
|
||||
tagsFromFile = loadTags();
|
||||
|
||||
table.setDisabled(true);
|
||||
}
|
||||
|
@ -86,20 +74,19 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Adds any selected tags to the function currently selected in the
|
||||
* listing.
|
||||
* Adds any selected tags to the function currently selected in the listing
|
||||
*/
|
||||
public void addSelectedTags() {
|
||||
if (function == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FunctionTag> selectedTags = getSelectedTags();
|
||||
Set<FunctionTag> selectedTags = getSelectedTags();
|
||||
for (FunctionTag tag : selectedTags) {
|
||||
|
||||
// If the tag is one that has not yet been created (a temp tag), first create it,
|
||||
// then add it to the function.
|
||||
if (tag instanceof FunctionTagTemp) {
|
||||
if (tag instanceof InMemoryFunctionTag) {
|
||||
Command cmd = new CreateFunctionTagCmd(tag.getName(), tag.getComment());
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
|
@ -110,57 +97,32 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Function function) {
|
||||
|
||||
public void refresh(Function newFunction) {
|
||||
model.clear();
|
||||
this.function = newFunction;
|
||||
table.setFunction(function);
|
||||
model.reload();
|
||||
}
|
||||
|
||||
this.function = function;
|
||||
|
||||
try {
|
||||
tempTags = new ArrayList<>(tempTagsCache);
|
||||
@Override
|
||||
protected Set<FunctionTag> backgroundLoadTags() {
|
||||
|
||||
List<? extends FunctionTag> dbTags = getAllTagsFromDatabase();
|
||||
for (FunctionTag tag : dbTags) {
|
||||
model.addTag(tag);
|
||||
}
|
||||
|
||||
// Now add any temp tags. Note that this is the point at which we prune the
|
||||
// temp tag list to remove any tags that have been added to the database. We
|
||||
// don't do it when the command for the add has been initiated, we do it here,
|
||||
// in response to getting the latest items from the db directly.
|
||||
Iterator<FunctionTag> iter = tempTags.iterator();
|
||||
while (iter.hasNext()) {
|
||||
FunctionTag tag = iter.next();
|
||||
Optional<? extends FunctionTag> foundTag = dbTags.stream()
|
||||
.filter(t -> t.getName().equals(tag.getName()))
|
||||
.findAny();
|
||||
if (foundTag.isPresent()) {
|
||||
iter.remove();
|
||||
}
|
||||
else {
|
||||
model.addTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
model.reload();
|
||||
applyFilter();
|
||||
table.setFunction(function);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this, "Error retrieving tags", e);
|
||||
}
|
||||
// Add any tags from the file system that are not in the db
|
||||
Set<FunctionTag> allTags = new HashSet<>(dbTags);
|
||||
allTags.addAll(tagsFromFile);
|
||||
return allTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all tags in the selection are enabled; false
|
||||
* otherwise
|
||||
* Returns true if all tags in the selection are enabled; false otherwise
|
||||
*
|
||||
* @return true if all tags in the selection are enabled; false
|
||||
* otherwise
|
||||
* @return true if all tags in the selection are enabled; false otherwise
|
||||
*/
|
||||
public boolean isSelectionEnabled() {
|
||||
List<FunctionTag> selectedTags = getSelectedTags();
|
||||
List<FunctionTag> assignedTags = getAssignedTags(function);
|
||||
Set<FunctionTag> selectedTags = getSelectedTags();
|
||||
Set<FunctionTag> assignedTags = getAssignedTags(function);
|
||||
if (assignedTags.containsAll(selectedTags)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -176,9 +138,8 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
* Returns an array of all tags stored in the database.
|
||||
*
|
||||
* @return list of tags
|
||||
* @throws IOException
|
||||
*/
|
||||
private List<? extends FunctionTag> getAllTagsFromDatabase() throws IOException {
|
||||
private List<? extends FunctionTag> getAllTagsFromDatabase() {
|
||||
if (program == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -191,7 +152,7 @@ public class SourceTagsPanel extends TagListPanel {
|
|||
*
|
||||
* @return the loaded tags
|
||||
*/
|
||||
private List<FunctionTag> loadTags() {
|
||||
private Set<FunctionTag> loadTags() {
|
||||
return FunctionTagLoader.loadTags(TAG_FILE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,31 +19,39 @@ import java.awt.BorderLayout;
|
|||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.DockingWindowManager;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import ghidra.app.cmd.function.ChangeFunctionTagCmd;
|
||||
import ghidra.app.cmd.function.DeleteFunctionTagCmd;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.table.GhidraTableFilterPanel;
|
||||
import ghidra.util.table.GhidraThreadedTablePanel;
|
||||
|
||||
/**
|
||||
* Base panel for displaying tags in the function tag window.
|
||||
*/
|
||||
public abstract class TagListPanel extends JPanel {
|
||||
|
||||
protected PluginTool tool;
|
||||
protected Program program;
|
||||
protected Function function;
|
||||
protected FunctionTagTable table;
|
||||
protected PluginTool tool;
|
||||
protected String filterString = "";
|
||||
|
||||
protected FunctionTagTableModel model;
|
||||
protected FunctionTagTableModel filteredModel;
|
||||
protected FunctionTagTable table;
|
||||
private GhidraTableFilterPanel<FunctionTagRowObject> filterPanel;
|
||||
|
||||
private JLabel titleLabel;
|
||||
|
||||
/**
|
||||
|
@ -58,10 +66,23 @@ public abstract class TagListPanel extends JPanel {
|
|||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
model = new FunctionTagTableModel("", provider.getTool());
|
||||
filteredModel = new FunctionTagTableModel("", provider.getTool());
|
||||
model = new FunctionTagTableModel("Function Tags", provider.getTool(),
|
||||
this::backgroundLoadTags);
|
||||
GhidraThreadedTablePanel<FunctionTagRowObject> tablePanel =
|
||||
new GhidraThreadedTablePanel<>(model) {
|
||||
protected GTable createTable(ThreadedTableModel<FunctionTagRowObject, ?> tm) {
|
||||
return new FunctionTagTable(model);
|
||||
}
|
||||
};
|
||||
table = (FunctionTagTable) tablePanel.getTable();
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, model);
|
||||
|
||||
titleLabel = new JLabel(title);
|
||||
titleLabel.setBorder(BorderFactory.createEmptyBorder(3, 5, 0, 0));
|
||||
add(titleLabel, BorderLayout.NORTH);
|
||||
add(tablePanel, BorderLayout.CENTER);
|
||||
add(filterPanel, BorderLayout.SOUTH);
|
||||
|
||||
table = new FunctionTagTable(filteredModel);
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
|
@ -77,77 +98,15 @@ public abstract class TagListPanel extends JPanel {
|
|||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
|
||||
FunctionTagTable table = (FunctionTagTable) evt.getSource();
|
||||
if (evt.getClickCount() != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.getClickCount() == 2) {
|
||||
int row = table.getSelectedRow();
|
||||
int nameCol = table.getColumnModel().getColumnIndex("Name");
|
||||
String tagName = (String) table.getValueAt(row, nameCol);
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
FunctionTag tag = model.getTag(tagName);
|
||||
if (tag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the tag is a temporary one, it's not editable. Show a message to the user.
|
||||
if (tag instanceof FunctionTagTemp) {
|
||||
Msg.showWarn(this, table, "Tag Not Editable",
|
||||
"Tag " + "\"" + tag.getName() + "\"" +
|
||||
" must be added to the program before it can be modified/deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
String[] labels = new String[] { "Name:", "Comment:" };
|
||||
String[] init = new String[] { tag.getName(), tag.getComment() };
|
||||
|
||||
InputDialog dialog = new InputDialog("Edit Tag", labels, init, true, d -> {
|
||||
String[] results = d.getValues();
|
||||
|
||||
if (results == null || results.length != 2) {
|
||||
Msg.error(this, "Error retrieving data from edit dialog"); // shouldn't happen
|
||||
return false;
|
||||
}
|
||||
|
||||
String newName = results[0].trim();
|
||||
String newComment = results[1].trim();
|
||||
|
||||
// If the name is empty, show a warning and don't allow it. A user should
|
||||
// never want to do this.
|
||||
if (newName.isEmpty()) {
|
||||
Msg.showWarn(this, table, "Empty Tag Name?",
|
||||
"Tag name cannot be empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only process the name edit if the name actually changed.
|
||||
if (!newName.equals(tag.getName())) {
|
||||
Command cmd = new ChangeFunctionTagCmd(tag.getName(), newName,
|
||||
ChangeFunctionTagCmd.TAG_NAME_CHANGED);
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
|
||||
// Only process the comment edit if the comment actually changed.
|
||||
if (!newComment.equals(tag.getComment())) {
|
||||
Command cmd = new ChangeFunctionTagCmd(tag.getName(), newComment,
|
||||
ChangeFunctionTagCmd.TAG_COMMENT_CHANGED);
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
DockingWindowManager.showDialog(tool.getActiveWindow(), dialog);
|
||||
|
||||
if (dialog.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
editRow(row);
|
||||
}
|
||||
});
|
||||
|
||||
titleLabel = new JLabel(title);
|
||||
titleLabel.setBorder(BorderFactory.createEmptyBorder(3, 5, 0, 0));
|
||||
add(titleLabel, BorderLayout.NORTH);
|
||||
add(new JScrollPane(table), BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -155,12 +114,87 @@ public abstract class TagListPanel extends JPanel {
|
|||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Clears the list and repopulates it with a new data set. Clients should override this
|
||||
* Clears the list and re-populates it with a new data set. Clients should override this
|
||||
* to retrieve data for the given function.
|
||||
*
|
||||
* @param function the currently selected function in the listing
|
||||
* @param newFunction the currently selected function in the listing
|
||||
*/
|
||||
public abstract void refresh(Function function);
|
||||
public abstract void refresh(Function newFunction);
|
||||
|
||||
/**
|
||||
* Called by a background thread to load the tags for this panel
|
||||
* @return the tags
|
||||
*/
|
||||
protected abstract Set<FunctionTag> backgroundLoadTags();
|
||||
|
||||
void editRow(int row) {
|
||||
|
||||
FunctionTagRowObject rowObject = model.getRowObject(row);
|
||||
if (rowObject.isImmutable()) {
|
||||
Msg.showWarn(this, table, "Tag Not Editable",
|
||||
"Tag " + "\"" + rowObject.getName() + "\"" +
|
||||
" must be added to the program before it can be modified/deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
String tagName = rowObject.getName();
|
||||
String comment = rowObject.getComment();
|
||||
String[] labels = new String[] { "Name:", "Comment:" };
|
||||
String[] init = new String[] { tagName, comment };
|
||||
InputDialog dialog = new InputDialog("Edit Tag", labels, init, true, d -> {
|
||||
String[] results = d.getValues();
|
||||
if (results == null || results.length != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String newName = results[0].trim();
|
||||
if (StringUtils.isBlank(newName)) {
|
||||
d.setStatusText("Tag name cannot be empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(tagName, newName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String newComment = results[1].trim();
|
||||
if (!Objects.equals(comment, newComment)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
DockingWindowManager.showDialog(tool.getActiveWindow(), dialog);
|
||||
if (dialog.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] results = dialog.getValues();
|
||||
String newName = results[0].trim();
|
||||
String newComment = results[1].trim();
|
||||
|
||||
// Only process the name edit if the name actually changed.
|
||||
if (!newName.equals(tagName)) {
|
||||
Command cmd = new ChangeFunctionTagCmd(tagName, newName,
|
||||
ChangeFunctionTagCmd.TAG_NAME_CHANGED);
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
|
||||
// Only process the comment edit if the comment actually changed.
|
||||
if (!newComment.equals(comment)) {
|
||||
Command cmd = new ChangeFunctionTagCmd(tagName, newComment,
|
||||
ChangeFunctionTagCmd.TAG_COMMENT_CHANGED);
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
}
|
||||
|
||||
FunctionTagTableModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
FunctionTagTable getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public void clearSelection() {
|
||||
table.clearSelection();
|
||||
|
@ -169,12 +203,6 @@ public abstract class TagListPanel extends JPanel {
|
|||
public void setProgram(Program program) {
|
||||
this.program = program;
|
||||
model.setProgram(program);
|
||||
filteredModel.setProgram(program);
|
||||
}
|
||||
|
||||
public void setFilterText(String text) {
|
||||
filterString = text;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
|
@ -188,7 +216,7 @@ public abstract class TagListPanel extends JPanel {
|
|||
* @return true if the tag exists
|
||||
*/
|
||||
public boolean tagExists(String name) {
|
||||
return model.isTagInModel(name);
|
||||
return model.containsTag(name);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -211,17 +239,8 @@ public abstract class TagListPanel extends JPanel {
|
|||
* @return true if list contains an immutable tag
|
||||
*/
|
||||
protected boolean isSelectionImmutable() {
|
||||
int[] selectedRows = table.getSelectedRows();
|
||||
int nameCol = table.getColumnModel().getColumnIndex("Name");
|
||||
for (int selectedRow : selectedRows) {
|
||||
String tagName = (String) table.getValueAt(selectedRow, nameCol);
|
||||
FunctionTag tag = filteredModel.getTag(tagName);
|
||||
if (tag instanceof FunctionTagTemp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
List<FunctionTagRowObject> items = filterPanel.getSelectedItems();
|
||||
return items.stream().anyMatch(row -> row.isImmutable());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,8 +248,7 @@ public abstract class TagListPanel extends JPanel {
|
|||
*/
|
||||
protected void deleteSelectedTags() {
|
||||
|
||||
List<FunctionTag> selectedTags = getSelectedTags();
|
||||
|
||||
Set<FunctionTag> selectedTags = getSelectedTags();
|
||||
if (selectedTags.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -241,45 +259,22 @@ public abstract class TagListPanel extends JPanel {
|
|||
"Are you sure? \nThis will delete the tag from all functions in the program.", "OK",
|
||||
OptionDialog.WARNING_MESSAGE);
|
||||
|
||||
switch (option) {
|
||||
case OptionDialog.OPTION_ONE:
|
||||
if (option == OptionDialog.OPTION_ONE) {
|
||||
for (FunctionTag tag : selectedTags) {
|
||||
Command cmd = new DeleteFunctionTagCmd(tag.getName());
|
||||
tool.execute(cmd, program);
|
||||
}
|
||||
break;
|
||||
case OptionDialog.CANCEL_OPTION:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list with the current filter settings
|
||||
*/
|
||||
protected void applyFilter() {
|
||||
filteredModel.clear();
|
||||
|
||||
for (FunctionTag tag : model.getTags()) {
|
||||
if (filterString.isEmpty()) {
|
||||
filteredModel.addTag(tag);
|
||||
}
|
||||
else if (tag.getName().toLowerCase().contains(filterString.toLowerCase())) {
|
||||
filteredModel.addTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
filteredModel.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all tags that have been assigned to the given function
|
||||
*
|
||||
* @param func the function to get tags for
|
||||
* @return list of all tags assigned to this function
|
||||
*/
|
||||
protected List<FunctionTag> getAssignedTags(Function func) {
|
||||
List<FunctionTag> assignedTags = new ArrayList<>();
|
||||
protected Set<FunctionTag> getAssignedTags(Function func) {
|
||||
Set<FunctionTag> assignedTags = new HashSet<>();
|
||||
if (func != null) {
|
||||
assignedTags.addAll(func.getTags());
|
||||
}
|
||||
|
@ -291,18 +286,8 @@ public abstract class TagListPanel extends JPanel {
|
|||
*
|
||||
* @return the list of function tags
|
||||
*/
|
||||
protected List<FunctionTag> getSelectedTags() {
|
||||
List<FunctionTag> tags = new ArrayList<>();
|
||||
int[] selectedIndices = table.getSelectedRows();
|
||||
for (int i : selectedIndices) {
|
||||
String tagName = (String) filteredModel.getValueAt(i, 0);
|
||||
Optional<FunctionTag> tag =
|
||||
filteredModel.getTags().stream().filter(t -> t.getName().equals(tagName)).findAny();
|
||||
if (tag.isPresent()) {
|
||||
tags.add(tag.get());
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
protected Set<FunctionTag> getSelectedTags() {
|
||||
List<FunctionTagRowObject> items = filterPanel.getSelectedItems();
|
||||
return items.stream().map(row -> row.getTag()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.cmd.function.RemoveFunctionTagCmd;
|
||||
import ghidra.framework.cmd.Command;
|
||||
|
@ -49,35 +48,33 @@ public class TargetTagsPanel extends TagListPanel {
|
|||
******************************************************************************/
|
||||
|
||||
@Override
|
||||
public void refresh(Function function) {
|
||||
public void refresh(Function newFunction) {
|
||||
|
||||
model.clear();
|
||||
|
||||
this.function = function;
|
||||
this.function = newFunction;
|
||||
|
||||
if (function == null) {
|
||||
setTitle("No Function Selected");
|
||||
}
|
||||
else {
|
||||
setTitle(function.getName() + " " + "(" + function.getEntryPoint().toString() + ")");
|
||||
setTitle(function.getName() + " (" + function.getEntryPoint().toString() + ')');
|
||||
}
|
||||
|
||||
List<FunctionTag> assignedTags = getAssignedTags(function);
|
||||
Collections.sort(assignedTags);
|
||||
for (FunctionTag tag : assignedTags) {
|
||||
model.addTag(tag);
|
||||
}
|
||||
|
||||
model.reload();
|
||||
applyFilter();
|
||||
table.setFunction(function);
|
||||
model.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<FunctionTag> backgroundLoadTags() {
|
||||
return getAssignedTags(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes selected tags from the currently-selected function.
|
||||
*/
|
||||
public void removeSelectedTags() {
|
||||
List<FunctionTag> selectedTags = getSelectedTags();
|
||||
Set<FunctionTag> selectedTags = getSelectedTags();
|
||||
for (FunctionTag tag : selectedTags) {
|
||||
Command cmd = new RemoveFunctionTagCmd(tag.getName(), function.getEntryPoint());
|
||||
tool.execute(cmd, program);
|
||||
|
|
|
@ -15,43 +15,30 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.textfield.HintTextField;
|
||||
import ghidra.app.cmd.function.ChangeFunctionTagCmd;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.function.FunctionDB;
|
||||
import ghidra.program.database.function.FunctionManagerDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionTag;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
|
@ -82,10 +69,6 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// The UI we're testing.
|
||||
private FunctionTagsComponentProvider provider = null;
|
||||
|
||||
/****************************************************************************************
|
||||
* SETUP/TEARDOWN
|
||||
****************************************************************************************/
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
|
@ -138,7 +121,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
cb.goTo(new ProgramLocation(program, NON_FUNCTION_ADDRESS));
|
||||
assertEquals(NON_FUNCTION_ADDRESS, cb.getCurrentAddress());
|
||||
ActionContext actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(!editFunctionTags.isEnabledForContext(actionContext));
|
||||
assertFalse(editFunctionTags.isEnabledForContext(actionContext));
|
||||
|
||||
cb.goToField(FUNCTION_ENTRY_ADDRESS, "Function Signature", 0, 0);
|
||||
assertEquals(FUNCTION_ENTRY_ADDRESS, cb.getCurrentAddress());
|
||||
|
@ -164,7 +147,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
// Get an immutable tag from the source panel and set it to be selected. Verify that
|
||||
// the delete button is disabled.
|
||||
FunctionTagTemp immutableTag = getImmutableTag();
|
||||
InMemoryFunctionTag immutableTag = getImmutableTag();
|
||||
assertTrue("Must have at least one immutable tag for this test", immutableTag != null);
|
||||
selectTagInList(immutableTag.getName(), table);
|
||||
waitForSwing();
|
||||
|
@ -175,7 +158,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
String tagName = "TAG 1";
|
||||
createTag(tagName);
|
||||
waitForSwing();
|
||||
table.addRowSelectionInterval(0, table.getRowCount()-1);
|
||||
table.addRowSelectionInterval(0, table.getRowCount() - 1);
|
||||
waitForSwing();
|
||||
assertFalse(isButtonEnabled("deleteBtn"));
|
||||
|
||||
|
@ -197,7 +180,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
FunctionTagTable table = getTagListInPanel("targetPanel");
|
||||
|
||||
// Get an immutable tag from the source panel.
|
||||
FunctionTagTemp tag = getImmutableTag();
|
||||
InMemoryFunctionTag tag = getImmutableTag();
|
||||
assertTrue("Must have at least one immutable tag for this test", tag != null);
|
||||
|
||||
// Assign the tag to a function, select the tag in the target panel,
|
||||
|
@ -205,7 +188,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
addTagToFunction(tag.getName(), FUNCTION_ENTRY_ADDRESS);
|
||||
waitForSwing();
|
||||
|
||||
boolean inList = isTagNameInList(tag.getName(), getAllTags());
|
||||
boolean inList = tagExists(tag.getName(), getAllTags());
|
||||
assertTrue(inList);
|
||||
|
||||
selectTagInList(tag.getName(), table);
|
||||
|
@ -232,18 +215,18 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// First verify that our function does not already contain the tags we're going
|
||||
// to add.
|
||||
Collection<? extends FunctionTag> tags = getAllTags();
|
||||
assertTrue(!isTagNameInList(tagName1, tags));
|
||||
assertTrue(!isTagNameInList(tagName2, tags));
|
||||
assertTrue(!isTagNameInList(tagName3, tags));
|
||||
assertFalse(tagExists(tagName1, tags));
|
||||
assertFalse(tagExists(tagName2, tags));
|
||||
assertFalse(tagExists(tagName3, tags));
|
||||
|
||||
// Now add them.
|
||||
createTag(tagName1 + ", " + tagName2 + " ,, " + tagName3);
|
||||
|
||||
// Check the database to verify that the tags were added correctly.
|
||||
tags = getAllTags();
|
||||
assertTrue(isTagNameInList(tagName1, tags));
|
||||
assertTrue(isTagNameInList(tagName2, tags));
|
||||
assertTrue(isTagNameInList(tagName3, tags));
|
||||
assertTrue(tagExists(tagName1, tags));
|
||||
assertTrue(tagExists(tagName2, tags));
|
||||
assertTrue(tagExists(tagName3, tags));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,10 +241,10 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
// First add a tag (so we have something to delete).
|
||||
createTag(name);
|
||||
assertTrue(isTagNameInList(name, getAllTags()));
|
||||
assertTrue(tagExists(name, getAllTags()));
|
||||
|
||||
deleteTag(name);
|
||||
assertTrue(!isTagNameInList(name, getAllTags()));
|
||||
assertFalse(tagExists(name, getAllTags()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,14 +260,14 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
// Add a tag and verify that it was correctly added to the database.
|
||||
createTag(oldName);
|
||||
assertTrue(isTagNameInList(oldName, getAllTags()));
|
||||
assertTrue(tagExists(oldName, getAllTags()));
|
||||
|
||||
// Update the tag name.
|
||||
updateTagName(oldName, newName);
|
||||
|
||||
// Verify that the old name is no longer in the db, but the new name is.
|
||||
assertTrue(!isTagNameInList(oldName, getAllTags()));
|
||||
assertTrue(isTagNameInList(newName, getAllTags()));
|
||||
assertFalse(tagExists(oldName, getAllTags()));
|
||||
assertTrue(tagExists(newName, getAllTags()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,7 +280,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
String name = "TEST";
|
||||
createTag(name);
|
||||
addTagToFunction(name, FUNCTION_ENTRY_ADDRESS);
|
||||
assertTrue(isTagNameInList(name, getTagsForFunctionAt(FUNCTION_ENTRY_ADDRESS)));
|
||||
assertTrue(tagExists(name, getTags(FUNCTION_ENTRY_ADDRESS)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,7 +293,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
createTag(name);
|
||||
addTagToFunction(name, FUNCTION_ENTRY_ADDRESS);
|
||||
removeTagFromFunction(name, FUNCTION_ENTRY_ADDRESS);
|
||||
assertTrue(!isTagNameInList(name, getTagsForFunctionAt(FUNCTION_ENTRY_ADDRESS)));
|
||||
assertFalse(tagExists(name, getTags(FUNCTION_ENTRY_ADDRESS)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,7 +307,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Verify that the function panel is initially empty
|
||||
AllFunctionsPanel functionsPanel = getFunctionsPanel();
|
||||
List<Function> functions = functionsPanel.getFunctions();
|
||||
assert (functions.isEmpty());
|
||||
assertTrue(functions.isEmpty());
|
||||
|
||||
// Create a new tag and add it to a function
|
||||
String tagName1 = "TAG 1";
|
||||
|
@ -340,9 +323,9 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// we have exactly 1 match, and that the address is for the correct
|
||||
// function)
|
||||
functions = functionsPanel.getFunctions();
|
||||
assert (functions.size() == 1);
|
||||
assertTrue(functions.size() == 1);
|
||||
Function f = functions.get(0);
|
||||
assert (f.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
assertTrue(f.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +340,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Verify that the function panel is initially empty
|
||||
AllFunctionsPanel functionsPanel = getFunctionsPanel();
|
||||
List<Function> functions = functionsPanel.getFunctions();
|
||||
assert (functions.isEmpty());
|
||||
assertTrue(functions.isEmpty());
|
||||
|
||||
// Create a new tag and add it to both functions
|
||||
String tagName1 = "TAG 1";
|
||||
|
@ -374,11 +357,11 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// we have exactly 2 matches, and that the addresses are for the correct
|
||||
// functions)
|
||||
functions = functionsPanel.getFunctions();
|
||||
assert (functions.size() == 2);
|
||||
assertTrue(functions.size() == 2);
|
||||
Function f1 = functions.get(0);
|
||||
Function f2 = functions.get(1);
|
||||
assert (f1.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
assert (f2.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_2));
|
||||
assertTrue(f1.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
assertTrue(f2.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_2));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,7 +376,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Verify that the function panel is initially empty
|
||||
AllFunctionsPanel functionsPanel = getFunctionsPanel();
|
||||
List<Function> functions = functionsPanel.getFunctions();
|
||||
assert (functions.isEmpty());
|
||||
assertTrue(functions.isEmpty());
|
||||
|
||||
// Create two new tags and add them to the functions
|
||||
String tagName1 = "TAG 1";
|
||||
|
@ -411,20 +394,20 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
FunctionTagTable table = getTagListInPanel("sourcePanel");
|
||||
selectTagInList(tagName1, table);
|
||||
int index = table.getSelectedRow();
|
||||
table.addRowSelectionInterval(index, index+1);
|
||||
table.addRowSelectionInterval(index, index + 1);
|
||||
clickTableRange(table, index, 2);
|
||||
|
||||
waitForTableModel(functionsPanel.getTableModel());
|
||||
|
||||
// Verify that all 3 functions are in the function panel
|
||||
functions = functionsPanel.getFunctions();
|
||||
assert (functions.size() == 3);
|
||||
assertTrue(functions.size() == 3);
|
||||
Function f1 = functions.get(0);
|
||||
Function f2 = functions.get(1);
|
||||
Function f3 = functions.get(2);
|
||||
assert (f1.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
assert (f2.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_2));
|
||||
assert (f3.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_3));
|
||||
assertTrue(f1.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS));
|
||||
assertTrue(f2.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_2));
|
||||
assertTrue(f3.getEntryPoint().equals(FUNCTION_ENTRY_ADDRESS_3));
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
|
@ -442,10 +425,33 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
* @param newName the new tag name
|
||||
*/
|
||||
private void updateTagName(String oldName, String newName) {
|
||||
Command cmd =
|
||||
new ChangeFunctionTagCmd(oldName, newName, ChangeFunctionTagCmd.TAG_NAME_CHANGED);
|
||||
tool.execute(cmd, program);
|
||||
|
||||
int row = getRow(oldName);
|
||||
SourceTagsPanel sourcePanel = provider.getSourcePanel();
|
||||
runSwing(() -> sourcePanel.editRow(row), false);
|
||||
|
||||
InputDialog dialog = waitForDialogComponent(InputDialog.class);
|
||||
runSwing(() -> {
|
||||
dialog.setValue(newName, 0);
|
||||
dialog.setValue("Some new comment", 1);
|
||||
});
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private int getRow(String tagName) {
|
||||
SourceTagsPanel sourcePanel = provider.getSourcePanel();
|
||||
FunctionTagTableModel model = sourcePanel.getModel();
|
||||
int n = model.getRowCount();
|
||||
for (int row = 0; row < n; row++) {
|
||||
FunctionTagRowObject rowObject = model.getRowObject(row);
|
||||
if (rowObject.getName().equals(tagName)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Could not find row for '" + tagName + "'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -523,16 +529,13 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
* @throws Exception if there is a problem clicking cells in a list
|
||||
*/
|
||||
private void selectTagInList(String name, FunctionTagTable table) throws Exception {
|
||||
FunctionTag tag = getListItemByName(name, table);
|
||||
if (tag == null) {
|
||||
throw new UsrException("Error retrieving tag with name: " + name);
|
||||
}
|
||||
assertTagExists(name, table);
|
||||
|
||||
int row = 0;
|
||||
for (int i=0; i<table.getRowCount(); i++) {
|
||||
for (int i = 0; i < table.getRowCount(); i++) {
|
||||
String tagname = (String) table.getValueAt(i, 0);
|
||||
if (tagname.equals(name)) {
|
||||
table.addRowSelectionInterval(i,i);
|
||||
table.addRowSelectionInterval(i, i);
|
||||
row = i;
|
||||
}
|
||||
}
|
||||
|
@ -574,22 +577,17 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return isEnabled(button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list item (FunctionTag) that has the given tag name.
|
||||
*
|
||||
* @param name the tag name
|
||||
* @param table the table to search
|
||||
*/
|
||||
private FunctionTag getListItemByName(String name, FunctionTagTable table) {
|
||||
int count = table.getRowCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
FunctionTagTableModel model = (FunctionTagTableModel)table.getModel();
|
||||
FunctionTag tag = model.getTag(name);
|
||||
if (tag.getName().equals(name)) {
|
||||
return tag;
|
||||
private FunctionTag assertTagExists(String name, FunctionTagTable table) {
|
||||
int rows = table.getRowCount();
|
||||
for (int row = 0; row < rows; row++) {
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
FunctionTagRowObject rowObject = model.getRowObject(row);
|
||||
if (rowObject.getName().equals(name)) {
|
||||
return rowObject.getTag();
|
||||
}
|
||||
}
|
||||
|
||||
fail("Error retrieving tag with name: " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -632,13 +630,22 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
*/
|
||||
private void createTag(String name) {
|
||||
|
||||
HintTextField inputField = (HintTextField) getInstanceField("tagInputTF", provider);
|
||||
HintTextField inputField = provider.getTagInputField();
|
||||
setText(inputField, name);
|
||||
triggerEnter(inputField);
|
||||
|
||||
waitForTables();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void waitForTables() {
|
||||
TargetTagsPanel targetPanel = provider.getTargetPanel();
|
||||
SourceTagsPanel sourcePanel = provider.getSourcePanel();
|
||||
FunctionTagTableModel tmodel = targetPanel.getModel();
|
||||
FunctionTagTableModel smodel = sourcePanel.getModel();
|
||||
waitForTableModel(tmodel);
|
||||
waitForTableModel(smodel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tag from the database.
|
||||
*
|
||||
|
@ -648,10 +655,8 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private void deleteTag(String name) throws IOException {
|
||||
|
||||
FunctionTag tag = getTagForName(name, getAllTags());
|
||||
int transactionID = program.startTransaction("delete function tag");
|
||||
tag.delete();
|
||||
program.endTransaction(transactionID, true);
|
||||
|
||||
tx(program, () -> tag.delete());
|
||||
waitForTables();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
@ -661,7 +666,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
* @param addr the function entry point
|
||||
* @return set of tags or null if function not found
|
||||
*/
|
||||
private Set<FunctionTag> getTagsForFunctionAt(Address addr) {
|
||||
private Set<FunctionTag> getTags(Address addr) {
|
||||
|
||||
FunctionDB function = (FunctionDB) program.getFunctionManager().getFunctionContaining(addr);
|
||||
if (function == null) {
|
||||
|
@ -684,16 +689,15 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
* @return an immutable tag, or null if not found
|
||||
* @throws UsrException if there's an error retrieving tags in the source panel
|
||||
*/
|
||||
private FunctionTagTemp getImmutableTag() throws UsrException {
|
||||
private InMemoryFunctionTag getImmutableTag() throws UsrException {
|
||||
|
||||
FunctionTagTable table = getTagListInPanel("sourcePanel");
|
||||
FunctionTagTableModel model = (FunctionTagTableModel)table.getModel();
|
||||
Optional<FunctionTag> foundTag = model.getTags().stream().filter(t -> t instanceof FunctionTagTemp).findAny();
|
||||
if (foundTag.isPresent()) {
|
||||
return (FunctionTagTemp)foundTag.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
Optional<FunctionTagRowObject> optional =
|
||||
model.getModelData().stream().filter(row -> row.isImmutable()).findAny();
|
||||
assertTrue("No Immutable tags found", optional.isPresent());
|
||||
FunctionTag foundTag = optional.get().getTag();
|
||||
return (InMemoryFunctionTag) foundTag;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -713,7 +717,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
* @param tags the list to inspect
|
||||
* @return true if found
|
||||
*/
|
||||
private boolean isTagNameInList(String name, Collection<? extends FunctionTag> tags) {
|
||||
private boolean tagExists(String name, Collection<? extends FunctionTag> tags) {
|
||||
FunctionTag tag = getTagForName(name, tags);
|
||||
return tag != null;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.tags;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -117,9 +117,9 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testLoadTags_EmptyFile() throws Exception {
|
||||
// Create file without contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
||||
|
@ -128,9 +128,9 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_EMPTY_TAGS.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
||||
|
@ -140,9 +140,9 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
File xxeFile = createTempFileForTest();
|
||||
xxeFile.delete();
|
||||
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
||||
|
@ -151,9 +151,9 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_MALFORMED_XML.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
||||
|
@ -168,17 +168,17 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_DEFAULT.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
expectedTags.add(new FunctionTagTemp("COMPRESSION", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CRYPTO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("DESTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("IO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("LIBRARY", ""));
|
||||
expectedTags.add(new FunctionTagTemp("NETWORK", ""));
|
||||
expectedTags.add(new FunctionTagTemp("UNPACKER", ""));
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
expectedTags.add(new InMemoryFunctionTag("COMPRESSION", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CRYPTO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("DESTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("IO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("LIBRARY", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("NETWORK", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("UNPACKER", ""));
|
||||
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
@ -189,16 +189,16 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_HAS_BLANK_NAME_VALUE.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
expectedTags.add(new FunctionTagTemp("COMPRESSION", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CRYPTO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("IO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("LIBRARY", ""));
|
||||
expectedTags.add(new FunctionTagTemp("NETWORK", ""));
|
||||
expectedTags.add(new FunctionTagTemp("UNPACKER", ""));
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
expectedTags.add(new InMemoryFunctionTag("COMPRESSION", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CRYPTO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("IO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("LIBRARY", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("NETWORK", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("UNPACKER", ""));
|
||||
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
@ -209,17 +209,17 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_HAS_COMMENT_VALUE.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
expectedTags.add(new FunctionTagTemp("COMPRESSION", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CRYPTO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("DESTRUCTOR", "IM A COMMENT"));
|
||||
expectedTags.add(new FunctionTagTemp("IO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("LIBRARY", ""));
|
||||
expectedTags.add(new FunctionTagTemp("NETWORK", ""));
|
||||
expectedTags.add(new FunctionTagTemp("UNPACKER", ""));
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
expectedTags.add(new InMemoryFunctionTag("COMPRESSION", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CRYPTO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("DESTRUCTOR", "IM A COMMENT"));
|
||||
expectedTags.add(new InMemoryFunctionTag("IO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("LIBRARY", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("NETWORK", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("UNPACKER", ""));
|
||||
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
@ -230,17 +230,17 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_NO_COMMENT_TAG.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
expectedTags.add(new FunctionTagTemp("COMPRESSION", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CRYPTO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("DESTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("IO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("LIBRARY", ""));
|
||||
expectedTags.add(new FunctionTagTemp("NETWORK", ""));
|
||||
expectedTags.add(new FunctionTagTemp("UNPACKER", ""));
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
expectedTags.add(new InMemoryFunctionTag("COMPRESSION", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CRYPTO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("DESTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("IO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("LIBRARY", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("NETWORK", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("UNPACKER", ""));
|
||||
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
@ -257,16 +257,16 @@ public class FunctionTagLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Create file with contents
|
||||
File xxeFile = createTempFileForTest();
|
||||
Files.write(xxeFile.toPath(), FUNCTION_TAGS_HAS_NO_NAME_VALUE.getBytes());
|
||||
List<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
Set<FunctionTag> tags = FunctionTagLoader.loadTags(xxeFile);
|
||||
|
||||
List<FunctionTag> expectedTags = new ArrayList<>();
|
||||
expectedTags.add(new FunctionTagTemp("COMPRESSION", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new FunctionTagTemp("CRYPTO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("IO", ""));
|
||||
expectedTags.add(new FunctionTagTemp("LIBRARY", ""));
|
||||
expectedTags.add(new FunctionTagTemp("NETWORK", ""));
|
||||
expectedTags.add(new FunctionTagTemp("UNPACKER", ""));
|
||||
Set<FunctionTag> expectedTags = new HashSet<>();
|
||||
expectedTags.add(new InMemoryFunctionTag("COMPRESSION", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CONSTRUCTOR", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("CRYPTO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("IO", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("LIBRARY", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("NETWORK", ""));
|
||||
expectedTags.add(new InMemoryFunctionTag("UNPACKER", ""));
|
||||
|
||||
assertEquals(tags, expectedTags);
|
||||
}
|
||||
|
|
|
@ -245,7 +245,16 @@ public class InputDialog extends DialogComponentProvider {
|
|||
* @param text the text
|
||||
*/
|
||||
public void setValue(String text) {
|
||||
textFields[0].setText(text);
|
||||
setValue(text, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of the text field at the given index
|
||||
* @param text the text
|
||||
* @param index the index of the text field
|
||||
*/
|
||||
public void setValue(String text, int index) {
|
||||
textFields[index].setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.util.datastruct;
|
||||
|
||||
/**
|
||||
* Simple class used to avoid immutable objects and autoboxing when storing changing integer
|
||||
* primitives in a collection.
|
||||
*/
|
||||
public class Counter {
|
||||
public int count;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(count);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.program.database.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import db.Record;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
|
@ -48,15 +49,17 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
|
|||
try {
|
||||
checkDeleted();
|
||||
|
||||
if (comment == null)
|
||||
if (comment == null) {
|
||||
comment = "";
|
||||
}
|
||||
|
||||
if (!comment.equals(record.getString(FunctionTagAdapter.COMMENT_COL))) {
|
||||
record.setString(FunctionTagAdapter.COMMENT_COL, comment);
|
||||
mgr.updateFunctionTag(this);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
mgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
|
@ -70,16 +73,19 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
|
|||
try {
|
||||
checkDeleted();
|
||||
|
||||
if (name == null)
|
||||
if (name == null) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
if (!name.equals(record.getString(FunctionTagAdapter.NAME_COL))) {
|
||||
record.setString(FunctionTagAdapter.NAME_COL, name);
|
||||
mgr.updateFunctionTag(this);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
mgr.dbError(e);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
mgr.lock.release();
|
||||
}
|
||||
}
|
||||
|
@ -149,8 +155,19 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) key;
|
||||
public void delete() {
|
||||
mgr.lock.acquire();
|
||||
try {
|
||||
if (checkIsValid()) {
|
||||
mgr.doDeleteTag(this);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
mgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
mgr.lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -163,36 +180,40 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ((obj == null) || (!(obj instanceof FunctionTag))) {
|
||||
return false;
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((getComment() == null) ? 0 : getComment().hashCode());
|
||||
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
|
||||
return result;
|
||||
}
|
||||
if (obj == this) {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
FunctionTag tag = (FunctionTag) obj;
|
||||
if (!getName().equals(tag.getName())) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!getComment().equals(tag.getComment())) {
|
||||
if (!(obj instanceof FunctionTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionTag other = (FunctionTag) obj;
|
||||
if (!Objects.equals(getComment(), other.getComment())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(getName(), other.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
mgr.lock.acquire();
|
||||
try {
|
||||
if (checkIsValid()) {
|
||||
mgr.doDeleteTag(this);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
mgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
mgr.lock.release();
|
||||
}
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,48 +17,42 @@ package ghidra.program.model.listing;
|
|||
|
||||
/**
|
||||
* Represents a function tag object that can be associated with
|
||||
* functions. This maps to the
|
||||
* {@link ghidra.program.database.function.FunctionTagAdapter FunctionTagAdapter} table.
|
||||
* functions. This maps to the {@code FunctionTagAdapter} table.
|
||||
*/
|
||||
public interface FunctionTag extends Comparable<FunctionTag> {
|
||||
|
||||
/**
|
||||
* Returns the id of the item.
|
||||
*
|
||||
* @return
|
||||
* Returns the id of the item
|
||||
* @return the id of the item
|
||||
*/
|
||||
public long getId();
|
||||
|
||||
/**
|
||||
* Returns the tag name.
|
||||
*
|
||||
* @return
|
||||
* Returns the tag name
|
||||
* @return the tag name
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns the tag comment.
|
||||
*
|
||||
* @return
|
||||
* Returns the tag comment
|
||||
* @return the tag comment
|
||||
*/
|
||||
public String getComment();
|
||||
|
||||
/**
|
||||
* Sets the name of the tag.
|
||||
*
|
||||
* Sets the name of the tag
|
||||
* @param name the tag name
|
||||
*/
|
||||
void setName(String name);
|
||||
public void setName(String name);
|
||||
|
||||
/**
|
||||
* Sets the comment for this tag.
|
||||
*
|
||||
* Sets the comment for this tag
|
||||
* @param comment the tag comment
|
||||
*/
|
||||
void setComment(String comment);
|
||||
public void setComment(String comment);
|
||||
|
||||
/**
|
||||
* Deletes this tag from the program.
|
||||
* Deletes this tag from the program
|
||||
*/
|
||||
void delete();
|
||||
public void delete();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.program.model.listing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package help.screenshot;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
|
||||
|
@ -26,15 +26,12 @@ import org.junit.Test;
|
|||
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import ghidra.app.plugin.core.function.tags.FunctionTagButtonPanel;
|
||||
import ghidra.app.plugin.core.function.tags.FunctionTagTableModel;
|
||||
import ghidra.app.plugin.core.function.tags.FunctionTagTable;
|
||||
import ghidra.app.plugin.core.function.tags.FunctionTagsComponentProvider;
|
||||
import ghidra.app.plugin.core.function.tags.SourceTagsPanel;
|
||||
import ghidra.app.plugin.core.function.tags.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
|
@ -138,7 +135,7 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
|
||||
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
|
||||
int row = -1;
|
||||
for (int i=0; i<model.getRowCount(); i++) {
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
String name = (String) table.getValueAt(i, 0);
|
||||
if (name.equals(text)) {
|
||||
row = i;
|
||||
|
@ -155,11 +152,13 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
private void addTableData() {
|
||||
|
||||
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
|
||||
|
||||
Swing.runNow(() -> {
|
||||
provider.programActivated(program);
|
||||
|
||||
navigateToFunction(provider);
|
||||
});
|
||||
|
||||
JTextField inputField = (JTextField) getInstanceField("tagInputTF", provider);
|
||||
JTextField inputField = (JTextField) getInstanceField("tagInputField", provider);
|
||||
setText(inputField, "Tag 2, Tag 3");
|
||||
triggerEnter(inputField);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue