GP-4162 fix view defined strings table filtering

This commit is contained in:
dev747368 2023-12-20 23:06:16 +00:00
parent 7864905bb6
commit 89762a9ef1
8 changed files with 167 additions and 214 deletions

View File

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.string.translate;
import java.util.*;
import java.util.List;
import docking.ActionContext;
import docking.action.DockingAction;
@ -63,13 +63,11 @@ public abstract class AbstractTranslateAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
if (context instanceof DataLocationListContext) {
DataLocationListContext dataContext = (DataLocationListContext) context;
actionPerformed(dataContext.getProgram(), getStringLocations(dataContext));
if (context instanceof DataLocationListContext dllc) {
actionPerformed(dllc.getProgram(), getStringLocations(dllc));
}
else if (context instanceof CodeViewerActionContext) {
CodeViewerActionContext codeContext = (CodeViewerActionContext) context;
actionPerformed(codeContext.getProgram(), getStringLocations(codeContext));
else if (context instanceof CodeViewerActionContext cvac) {
actionPerformed(cvac.getProgram(), getStringLocations(cvac));
}
else {
throw new AssertException("This can't happen!");
@ -91,9 +89,9 @@ public abstract class AbstractTranslateAction extends DockingAction {
protected List<ProgramLocation> getStringLocations(CodeViewerActionContext context) {
Data data = DataUtilities.getDataAtLocation(context.getLocation());
if (data == null || !StringDataInstance.isString(data)) {
return Collections.emptyList();
return List.of();
}
return Arrays.asList(context.getLocation());
return List.of(context.getLocation());
}
protected List<ProgramLocation> getStringLocations(DataLocationListContext context) {

View File

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.string.translate;
import static ghidra.program.model.data.TranslationSettingsDefinition.TRANSLATION;
import static ghidra.program.model.data.TranslationSettingsDefinition.*;
import java.util.List;
@ -64,6 +64,7 @@ public class ClearTranslationAction extends AbstractTranslateAction {
monitor.initialize(dataLocations.size());
for (ProgramLocation progLoc : dataLocations) {
Data data = DataUtilities.getDataAtLocation(progLoc);
TRANSLATION.setTranslatedValue(data, null);
TRANSLATION.clear(data);
}
}

View File

@ -1,29 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.strings;
import docking.widgets.table.constraint.ColumnTypeMapper;
import ghidra.program.model.data.StringDataInstance;
public class StringDataInstanceColumnTypeMapper
extends ColumnTypeMapper<StringDataInstance, String> {
@Override
public String convert(StringDataInstance value) {
return value.getStringValue();
}
}

View File

@ -15,32 +15,36 @@
*/
package ghidra.app.plugin.core.strings;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import docking.DefaultActionContext;
import ghidra.app.context.DataLocationListContext;
import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.table.GhidraTable;
public class ViewStringsContext extends DefaultActionContext implements DataLocationListContext {
private ViewStringsProvider viewStringsProvider;
private final ViewStringsProvider viewStringsProvider;
private final GhidraTable table;
private final ViewStringsTableModel tableModel;
ViewStringsContext(ViewStringsProvider provider, GhidraTable stringsTable) {
super(provider, stringsTable);
viewStringsProvider = provider;
}
GhidraTable getStringsTable() {
return (GhidraTable) getContextObject();
ViewStringsContext(ViewStringsProvider provider, GhidraTable table,
ViewStringsTableModel tableModel) {
super(provider, table);
this.viewStringsProvider = provider;
this.table = table;
this.tableModel = tableModel;
}
@Override
public int getCount() {
return viewStringsProvider.getSelectedRowCount();
return table.getSelectedRowCount();
}
@Override
@ -50,11 +54,48 @@ public class ViewStringsContext extends DefaultActionContext implements DataLoca
@Override
public List<ProgramLocation> getDataLocationList() {
return viewStringsProvider.getSelectedDataLocationList(null);
return getDataLocationList(null);
}
@Override
public List<ProgramLocation> getDataLocationList(Predicate<Data> filter) {
return viewStringsProvider.getSelectedDataLocationList(filter);
List<ProgramLocation> result = new ArrayList<>();
int[] selectedRows = table.getSelectedRows();
for (int row : selectedRows) {
ProgramLocation location = tableModel.getRowObject(row);
Data data = DataUtilities.getDataAtLocation(location);
if (passesFilter(data, filter)) {
result.add(location);
}
}
return result;
}
private boolean passesFilter(Data data, Predicate<Data> filter) {
if (data == null) {
return false;
}
if (filter == null) {
return true;
}
return filter.test(data);
}
ProgramSelection getProgramSelection() {
return table.getProgramSelection();
}
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
public Data getSelectedData() {
int selectedRow = table.getSelectedRow();
if (selectedRow < 0) {
return null;
}
ProgramLocation location = tableModel.getRowObject(selectedRow);
return DataUtilities.getDataAtLocation(location);
}
}

View File

@ -17,8 +17,8 @@ package ghidra.app.plugin.core.strings;
import javax.swing.Icon;
import docking.ActionContext;
import docking.action.*;
import docking.action.DockingAction;
import docking.action.builder.ActionBuilder;
import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
@ -86,40 +86,35 @@ public class ViewStringsPlugin extends ProgramPlugin implements DomainObjectList
}
private void createActions() {
refreshAction = new DockingAction("Refresh Strings", getName()) {
@Override
public boolean isEnabledForContext(ActionContext context) {
return getCurrentProgram() != null;
}
@Override
public void actionPerformed(ActionContext context) {
getToolBarData().setIcon(REFRESH_NOT_NEEDED_ICON);
reload();
}
};
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON));
refreshAction.setDescription(
"<html>Push at any time to refresh the current table of strings.<br>" +
"This button is highlighted when the data <i>may</i> be stale.<br>");
refreshAction.setHelpLocation(new HelpLocation("ViewStringsPlugin", "Refresh"));
tool.addLocalAction(provider, refreshAction);
refreshAction = new ActionBuilder("Refresh Strings", getName())
.toolBarIcon(REFRESH_NOT_NEEDED_ICON)
.description("<html>Push at any time to refresh the current table of strings.<br>" +
"This button is highlighted when the data <i>may</i> be stale.<br>")
.enabledWhen(ac -> getCurrentProgram() != null)
.onAction(ac -> {
refreshAction.getToolBarData().setIcon(REFRESH_NOT_NEEDED_ICON);
reload();
})
.helpLocation(new HelpLocation("ViewStringsPlugin", "Refresh"))
.buildAndInstallLocal(provider);
tool.addLocalAction(provider, new MakeProgramSelectionAction(this, provider.getTable()));
linkNavigationAction = new SelectionNavigationAction(this, provider.getTable());
tool.addLocalAction(provider, linkNavigationAction);
DockingAction editDataSettingsAction =
new DockingAction("Data Settings", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
new ActionBuilder("Data Settings", getName()) // create pop-up menu item "Settings..."
.withContext(ViewStringsContext.class)
.popupMenuPath("Settings...")
.popupMenuGroup("R")
.helpLocation(new HelpLocation("DataPlugin", "Data_Settings"))
.sharedKeyBinding()
.enabledWhen(vsac -> vsac.getCount() > 0)
.onAction(vsac -> {
try {
DataSettingsDialog dialog = provider.getSelectedRowCount() == 1
? new DataSettingsDialog(provider.getSelectedData())
: new DataSettingsDialog(currentProgram,
provider.getProgramSelection());
DataSettingsDialog dialog = vsac.getCount() == 1
? new DataSettingsDialog(vsac.getSelectedData())
: new DataSettingsDialog(vsac.getProgram(), vsac.getProgramSelection());
tool.showDialog(dialog);
dialog.dispose();
@ -127,51 +122,34 @@ public class ViewStringsPlugin extends ProgramPlugin implements DomainObjectList
catch (CancelledException e) {
// do nothing
}
}
})
.buildAndInstallLocal(provider);
};
editDataSettingsAction.setPopupMenuData(new MenuData(new String[] { "Settings..." }, "R"));
editDataSettingsAction.setHelpLocation(new HelpLocation("DataPlugin", "Data_Settings"));
DockingAction editDefaultSettingsAction =
new DockingAction("Default Settings", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
DataType dt = getSelectedDataType();
if (dt == null) {
new ActionBuilder("Default Settings", getName()) // create pop-up menu item "Default Settings..."
.withContext(ViewStringsContext.class)
.popupMenuPath("Default Settings...")
.popupMenuGroup("R")
.helpLocation(new HelpLocation("DataPlugin", "Default_Settings"))
.sharedKeyBinding()
.enabledWhen(vsac -> {
if (vsac.getCount() != 1) {
return false;
}
Data data = vsac.getSelectedData();
return data != null && data.getDataType().getSettingsDefinitions().length != 0;
})
.onAction(vsac -> {
Data data = vsac.getSelectedData();
if (data == null) {
return;
}
DataTypeSettingsDialog dataSettingsDialog =
DataType dt = data.getDataType();
DataTypeSettingsDialog dialog =
new DataTypeSettingsDialog(dt, dt.getSettingsDefinitions());
tool.showDialog(dataSettingsDialog);
dataSettingsDialog.dispose();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (provider.getSelectedRowCount() != 1) {
return false;
}
DataType dt = getSelectedDataType();
if (dt == null) {
return false;
}
return dt.getSettingsDefinitions().length != 0;
}
private DataType getSelectedDataType() {
Data data = provider.getSelectedData();
return data != null ? data.getDataType() : null;
}
};
editDefaultSettingsAction.setPopupMenuData(
new MenuData(new String[] { "Default Settings..." }, "R"));
editDefaultSettingsAction.setHelpLocation(
new HelpLocation("DataPlugin", "Default_Settings"));
tool.addLocalAction(provider, editDataSettingsAction);
tool.addLocalAction(provider, editDefaultSettingsAction);
tool.showDialog(dialog);
dialog.dispose();
})
.buildAndInstallLocal(provider);
}
@Override

View File

@ -17,15 +17,11 @@ package ghidra.app.plugin.core.strings;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import javax.swing.*;
import javax.swing.table.TableColumn;
import docking.ActionContext;
import docking.widgets.table.GTableTextCellEditor;
import docking.widgets.table.threaded.ThreadedTableModelListener;
import generic.theme.GIcon;
@ -39,7 +35,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.HelpLocation;
import ghidra.util.table.*;
import ghidra.util.task.TaskMonitor;
/**
* Provider for the defined strings table.
@ -76,8 +71,8 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
}
@Override
public ActionContext getActionContext(MouseEvent event) {
return new ViewStringsContext(this, table);
public ViewStringsContext getActionContext(MouseEvent event) {
return new ViewStringsContext(this, table, stringModel);
}
@Override
@ -125,8 +120,8 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
int rowCount = stringModel.getRowCount();
int unfilteredCount = stringModel.getUnfilteredRowCount();
setSubTitle("" + rowCount + " items" +
(rowCount != unfilteredCount ? " (of " + unfilteredCount + ")" : ""));
setSubTitle("%d items%s".formatted(rowCount,
rowCount != unfilteredCount ? " (of " + unfilteredCount + ")" : ""));
});
stringModel.addThreadedTableModelListener(new ThreadedTableModelListener() {
@ -181,7 +176,7 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
void add(Data data) {
if (isVisible()) {
stringModel.addDataInstance(currentProgram, data, TaskMonitor.DUMMY);
stringModel.addDataInstance(currentProgram, data);
}
}
@ -242,54 +237,6 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
}
}
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
public Data getSelectedData() {
int selectedRow = table.getSelectedRow();
if (selectedRow < 0) {
return null;
}
ProgramLocation location = stringModel.getRowObject(selectedRow);
return DataUtilities.getDataAtLocation(location);
}
public List<Data> getSelectedDataList(Predicate<Data> filter) {
List<Data> list = new ArrayList<>();
int[] selectedRows = table.getSelectedRows();
for (int row : selectedRows) {
ProgramLocation location = stringModel.getRowObject(row);
Data data = DataUtilities.getDataAtLocation(location);
if (passesFilter(data, filter)) {
list.add(data);
}
}
return list;
}
public List<ProgramLocation> getSelectedDataLocationList(Predicate<Data> filter) {
List<ProgramLocation> result = new ArrayList<>();
int[] selectedRows = table.getSelectedRows();
for (int row : selectedRows) {
ProgramLocation location = stringModel.getRowObject(row);
Data data = DataUtilities.getDataAtLocation(location);
if (passesFilter(data, filter)) {
result.add(location);
}
}
return result;
}
private boolean passesFilter(Data data, Predicate<Data> filter) {
if (data == null) {
return false;
}
if (filter == null) {
return true;
}
return filter.test(data);
}
public Program getProgram() {
return currentProgram;
@ -322,10 +269,11 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
@Override
public Component getTableCellEditorComponent(JTable jTable, Object value,
boolean isSelected, int row, int column) {
if (value instanceof StringDataInstance) {
boolean isSelected, int rowIndex, int columnIndex) {
Data data = DataUtilities.getDataAtLocation(stringModel.getRowObject(rowIndex));
if (data != null) {
textField.setEditable(true);
StringDataInstance sdi = (StringDataInstance) value;
StringDataInstance sdi = StringDataInstance.getStringDataInstance(data);
if (sdi.isShowTranslation() && sdi.getTranslatedValue() != null) {
textField.setText(sdi.getTranslatedValue());
}
@ -334,8 +282,8 @@ public class ViewStringsProvider extends ComponentProviderAdapter {
}
}
else {
textField.setText("");
textField.setEditable(false);
textField.setText("unsupported");
}
return textField;
}

View File

@ -62,7 +62,8 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
IS_ASCII_COL,
CHARSET_COL,
HAS_ENCODING_ERROR,
UNICODE_SCRIPT
UNICODE_SCRIPT,
TRANSLATED_VALUE
}
ViewStringsTableModel(PluginTool tool) {
@ -81,7 +82,10 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
DynamicTableColumn<ProgramLocation, ?, ?> column = getColumn(columnIndex);
if (column instanceof StringRepColumn) {
ProgramLocation progLoc = getRowObject(rowIndex);
ManualStringTranslationService.setTranslatedValue(program, progLoc, aValue.toString());
if (progLoc != null) {
ManualStringTranslationService.setTranslatedValue(program, progLoc,
aValue.toString());
}
}
}
@ -92,12 +96,13 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
// These columns need to match the COLUMNS enum indexes
descriptor.addVisibleColumn(new DataLocationColumn(), 1, true);
descriptor.addVisibleColumn(new DataValueColumn());
descriptor.addVisibleColumn(new StringRepColumn());
descriptor.addVisibleColumn(new StringRepColumn()); // see StringRepCellEditor in ViewStringsProvider
descriptor.addVisibleColumn(new DataTypeColumn());
descriptor.addHiddenColumn(new IsAsciiColumn());
descriptor.addHiddenColumn(new CharsetColumn());
descriptor.addHiddenColumn(new HasEncodingErrorColumn());
descriptor.addHiddenColumn(new UnicodeScriptColumn());
descriptor.addHiddenColumn(new TranslatedValueColumn());
return descriptor;
}
@ -119,8 +124,7 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
Swing.allowSwingToProcessEvents();
for (Data stringInstance : DefinedDataIterator.definedStrings(localProgram)) {
accumulator.add(createIndexedStringInstanceLocation(localProgram, stringInstance));
monitor.checkCancelled();
monitor.incrementProgress(1);
monitor.increment();
}
}
@ -143,7 +147,7 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
return (pl != null) ? rowsIndexedByAddress.get(pl.getAddress()) : null;
}
public void addDataInstance(Program localProgram, Data data, TaskMonitor monitor) {
public void addDataInstance(Program localProgram, Data data) {
for (Data stringInstance : DefinedDataIterator.definedStrings(data)) {
addObject(createIndexedStringInstanceLocation(localProgram, stringInstance));
}
@ -202,6 +206,9 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
private static class DataValueColumn
extends AbstractProgramLocationTableColumn<ProgramLocation, StringDataInstance> {
// Also see ViewStringsColumnConstrainProvider for filtering constraints that operate
// on the value of this column
private DataValueCellRenderer renderer = new DataValueCellRenderer();
@Override
@ -241,14 +248,19 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
public String getFilterString(StringDataInstance t, Settings settings) {
return getText(t);
}
@Override
public ColumnConstraintFilterMode getColumnConstraintFilterMode() {
return ColumnConstraintFilterMode.ALLOW_ALL_FILTERS;
}
}
}
private static class StringRepColumn
extends AbstractProgramLocationTableColumn<ProgramLocation, StringDataInstance> {
extends AbstractProgramLocationTableColumn<ProgramLocation, String> {
private StringRepCellRenderer renderer = new StringRepCellRenderer();
// also see StringRepCellEditor in ViewStringsProvider
@Override
public String getColumnName() {
@ -256,12 +268,12 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
}
@Override
public StringDataInstance getValue(ProgramLocation rowObject, Settings settings,
public String getValue(ProgramLocation rowObject, Settings settings,
Program program, ServiceProvider serviceProvider) throws IllegalArgumentException {
Data data = DataUtilities.getDataAtLocation(rowObject);
if (StringDataInstance.isString(data)) {
StringDataInstance sdi = StringDataInstance.getStringDataInstance(data);
return sdi;
return sdi.getStringRepresentation();
}
return null;
}
@ -271,27 +283,6 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
Program program, ServiceProvider serviceProvider) {
return rowObject;
}
@Override
public GColumnRenderer<StringDataInstance> getColumnRenderer() {
return renderer;
}
private class StringRepCellRenderer extends AbstractGColumnRenderer<StringDataInstance> {
@Override
protected String getText(Object value) {
if (value instanceof StringDataInstance) {
return ((StringDataInstance) value).getStringRepresentation();
}
return "";
}
@Override
public String getFilterString(StringDataInstance t, Settings settings) {
return getText(t);
}
}
}
// data type to string column
@ -402,6 +393,31 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
}
private static class TranslatedValueColumn
extends AbstractProgramLocationTableColumn<ProgramLocation, String> {
@Override
public String getColumnName() {
return "Translated Value";
}
@Override
public String getValue(ProgramLocation rowObject, Settings settings, Program program,
ServiceProvider serviceProvider) throws IllegalArgumentException {
Data data = DataUtilities.getDataAtLocation(rowObject);
String s = TranslationSettingsDefinition.TRANSLATION.getTranslatedValue(data);
return s;
}
@Override
public ProgramLocation getProgramLocation(ProgramLocation rowObject, Settings settings,
Program program, ServiceProvider serviceProvider) {
return rowObject;
}
}
private static class UnicodeScriptColumn
extends AbstractProgramLocationTableColumn<ProgramLocation, String> {

View File

@ -37,7 +37,7 @@ public class TranslationSettingsDefinition extends JavaEnumSettingsDefinition<TR
private final String s;
private TRANSLATION_ENUM(String s) {
TRANSLATION_ENUM(String s) {
this.s = s;
}