Merge remote-tracking branch 'origin/GP-4853_dev747368_datatype_select_nodups'

This commit is contained in:
Ryan Kurtz 2024-11-07 11:47:11 -05:00
commit ad86d2d65a
12 changed files with 156 additions and 88 deletions

View File

@ -128,8 +128,8 @@ public class DataTypeTableCellEditor extends AbstractCellEditor
protected void init(int row, int column) {
updateService();
editor = new DataTypeSelectionEditor(service, getAllowed(row, column));
editor.setPreferredDataTypeManager(getPreferredDataTypeManager(row, column));
editor = new DataTypeSelectionEditor(getPreferredDataTypeManager(row, column), service,
getAllowed(row, column));
editor.setTabCommitsEdit(true);
editor.setConsumeEnterKeyPress(false);

View File

@ -255,8 +255,8 @@ public class BitFieldEditorPanel extends JPanel {
private JComponent createDataTypeChoiceEditor() {
dtChoiceEditor =
new DataTypeSelectionEditor(dtmService, AllowedDataTypes.BITFIELD_BASE_TYPE);
dtChoiceEditor = new DataTypeSelectionEditor(composite.getDataTypeManager(), dtmService,
AllowedDataTypes.BITFIELD_BASE_TYPE);
dtChoiceEditor.setConsumeEnterKeyPress(false);
dtChoiceEditor.setTabCommitsEdit(true);
//dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager());

View File

@ -1201,13 +1201,12 @@ public abstract class CompositeEditorPanel extends JPanel
private void init() {
Plugin plugin = provider.getPlugin();
final PluginTool tool = plugin.getTool();
editor = new DataTypeSelectionEditor(tool,
bitfieldAllowed ? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD
PluginTool tool = plugin.getTool();
editor = new DataTypeSelectionEditor(model.getViewDataTypeManager(), tool,
bitfieldAllowed
? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD
: AllowedDataTypes.SIZABLE_DYNAMIC);
editor.setTabCommitsEdit(true);
DataTypeManager originalDataTypeManager = model.getOriginalDataTypeManager();
editor.setPreferredDataTypeManager(originalDataTypeManager);
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses
textField = editor.getDropDownTextField();

View File

@ -15,6 +15,9 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
@ -56,6 +59,11 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
}
private JComponent createWorkPanel() {
List<DataTypeManager> managers = Arrays.stream(plugin.getDataTypeManagers())
.filter(dtm -> !(dtm instanceof BuiltInDataTypeManager))
.toList();
DataTypeManager defaultDTM = getDefaultDataTypeManager(managers);
JPanel panel = new JPanel(new PairLayout());
// category info
@ -68,7 +76,9 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
panel.add(nameTextField);
// data type info
dataTypeEditor = new DataTypeSelectionEditor(plugin.getTool(), AllowedDataTypes.ALL);
dataTypeEditor = new DataTypeSelectionEditor(
null, /* TODO: can't set default dtm for the data type selection field because the dialog allows switching between destination DTMs */
plugin.getTool(), AllowedDataTypes.ALL);
panel.add(new GLabel("Data type:"));
panel.add(dataTypeEditor.getEditorComponent());
@ -89,29 +99,8 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
dataTypeManagerBox = new GhidraComboBox<>();
dataTypeManagerBox
.setRenderer(GComboBoxCellRenderer.createDefaultTextRenderer(dtm -> dtm.getName()));
DataTypeManager[] dataTypeManagers = plugin.getDataTypeManagers();
for (DataTypeManager manager : dataTypeManagers) {
if (manager instanceof BuiltInDataTypeManager) {
continue; // can't add to built-in
}
dataTypeManagerBox.addToModel(manager);
}
Object itemToSelect = null;
// select the manager from where the dialog was created
Object lastPathComponent = selectedTreePath.getLastPathComponent();
if (lastPathComponent instanceof DataTypeTreeNode) {
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) lastPathComponent;
ArchiveNode archiveNode = dataTypeTreeNode.getArchiveNode();
DataTypeManager manager = archiveNode.getArchive().getDataTypeManager();
if (dataTypeManagerBox.containsItem(manager)) {
itemToSelect = manager;
}
}
dataTypeManagerBox.setSelectedItem(itemToSelect);
dataTypeManagerBox.addToModel(managers);
dataTypeManagerBox.setSelectedItem(defaultDTM);
panel.add(new GLabel("Archive:"));
panel.add(dataTypeManagerBox);
@ -121,6 +110,19 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
return panel;
}
private DataTypeManager getDefaultDataTypeManager(List<DataTypeManager> mgrs) {
// select the manager from where the dialog was created
Object lastPathComponent = selectedTreePath.getLastPathComponent();
if (lastPathComponent instanceof DataTypeTreeNode dataTypeTreeNode) {
ArchiveNode archiveNode = dataTypeTreeNode.getArchiveNode();
DataTypeManager manager = archiveNode.getArchive().getDataTypeManager();
if (mgrs.contains(manager)) {
return manager;
}
}
return null;
}
@Override
protected void okCallback() {
// are we valid?

View File

@ -759,8 +759,8 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
getSelectionModel().addListSelectionListener(selectionListener);
// set the preferred viewport height smaller that the button panel, otherwise it is huge!
setPreferredScrollableViewportSize(new Dimension(600, 100));
setDefaultEditor(DataType.class,
new ParameterDataTypeCellEditor(FunctionEditorDialog.this, service));
setDefaultEditor(DataType.class, new ParameterDataTypeCellEditor(
FunctionEditorDialog.this, service, model.getProgram().getDataTypeManager()));
setDefaultRenderer(DataType.class, new ParameterDataTypeCellRenderer());
setDefaultEditor(VariableStorage.class, new StorageTableCellEditor(model));
setDefaultRenderer(VariableStorage.class, new VariableStorageCellRenderer());

View File

@ -30,6 +30,7 @@ import docking.widgets.table.FocusableEditor;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.util.MessageType;
import ghidra.util.data.DataTypeParser;
@ -43,27 +44,29 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
private JPanel editorPanel;
private DataTypeManagerService service;
private DialogComponentProvider dialog;
private DataTypeManager dtm;
ParameterDataTypeCellEditor(DialogComponentProvider dialog, DataTypeManagerService service) {
ParameterDataTypeCellEditor(DialogComponentProvider dialog, DataTypeManagerService service,
DataTypeManager dtm) {
this.dialog = dialog;
this.service = service;
this.dtm = dtm;
}
@Override
public Component getTableCellEditorComponent(JTable table1, Object value, boolean isSelected,
int row, int column) {
init();
dt = (DataType) value;
init();
editor.setCellEditorValue(dt);
return editorPanel;
}
private void init() {
editor = new DataTypeSelectionEditor(service, DataTypeParser.AllowedDataTypes.ALL);
editor = new DataTypeSelectionEditor(dtm, service, DataTypeParser.AllowedDataTypes.ALL);
editor.setTabCommitsEdit(true);
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses

View File

@ -172,7 +172,8 @@ public class StorageAddressEditorDialog extends DialogComponentProvider
panel.add(new GLabel("Datatype: "));
dataTypeEditor = new ParameterDataTypeCellEditor(this, service);
dataTypeEditor =
new ParameterDataTypeCellEditor(this, service, model.getProgram().getDataTypeManager());
dataTypeEditor.addCellEditorListener(new CellEditorListener() {

View File

@ -23,11 +23,14 @@ import javax.swing.*;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.DropDownTextFieldDataModel;
import docking.widgets.list.GListCellRenderer;
import ghidra.app.plugin.core.compositeeditor.CompositeViewerDataTypeManager;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.ToolTipUtils;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
/**
@ -36,13 +39,25 @@ import ghidra.util.exception.AssertException;
*/
public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldDataModel<DataType> {
private final DataTypeManager dtm; // preferred data type manager
private final DataTypeManagerService dataTypeService;
public DataTypeDropDownSelectionDataModel(ServiceProvider serviceProvider) {
this.dtm = null;
this.dataTypeService = getDataTypeService(serviceProvider);
}
public DataTypeDropDownSelectionDataModel(DataTypeManagerService dataTypeService) {
/**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Data types that are found in multiple
* data type managers will be pruned to just the ones already in the preferred data type
* manager.
* @param dataTypeService {@link DataTypeManagerService}
*/
public DataTypeDropDownSelectionDataModel(DataTypeManager dtm,
DataTypeManagerService dataTypeService) {
this.dtm = dtm;
this.dataTypeService = dataTypeService;
}
@ -84,16 +99,57 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
* Remove any unwanted data type items, like arrays.
*/
private List<DataType> filterDataTypeList(List<DataType> dataTypeList) {
List<DataType> matchingList = new ArrayList<>(dataTypeList.size());
for (DataType dataType : dataTypeList) {
if (!(dataType instanceof Array)) {
matchingList.add(dataType);
// build lookups for data types that are in the preferred dtm, but may have come from
// another DTM. In the second step, duplicate data types will be omitted from the
// final results, in favor of the data type that is already in the preferred dtm.
Set<UniversalID> preferredUIDs = new HashSet<>();
Set<Class<?>> preferredBuiltins = new HashSet<>();
for (DataType dt : dataTypeList) {
DataType baseDT = DataTypeUtilities.getBaseDataType(dt);
if (isFromPreferredDTM(baseDT)) {
if (baseDT instanceof BuiltInDataType) {
preferredBuiltins.add(baseDT.getClass());
}
else if (baseDT.getUniversalID() != null) {
preferredUIDs.add(baseDT.getUniversalID());
}
}
}
List<DataType> matchingList = new ArrayList<>(dataTypeList.size());
for (DataType dataType : dataTypeList) {
if (dataType instanceof Array) {
continue;
}
DataType baseDT = DataTypeUtilities.getBaseDataType(dataType);
if (dtm != null && !isFromPreferredDTM(baseDT)) {
if (baseDT instanceof BuiltInDataType &&
preferredBuiltins.contains(baseDT.getClass())) {
continue;
}
if (baseDT.getUniversalID() != null &&
preferredUIDs.contains(baseDT.getUniversalID())) {
continue;
}
}
matchingList.add(dataType);
}
return matchingList;
}
private boolean isFromPreferredDTM(DataType dt) {
if (dtm != null) {
DataTypeManager altDTM = dtm instanceof CompositeViewerDataTypeManager compDTM
? compDTM.getOriginalDataTypeManager()
: null;
DataTypeManager dtDTM = dt.getDataTypeManager();
return dtDTM == dtm || dtDTM == altDTM;
}
return false;
}
@Override
public int getIndexOfFirstMatchingEntry(List<DataType> data, String text) {

View File

@ -66,7 +66,6 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
removeWorkPanel();
editor = createEditor(pluginTool, allowedTypes);
editor.setPreferredDataTypeManager(dtm);
editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses
editor.addCellEditorListener(new CellEditorListener() {
@Override
@ -110,7 +109,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
protected DataTypeSelectionEditor createEditor(PluginTool tool,
AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(tool, allowedDataTypes);
return new DataTypeSelectionEditor(dtm, tool, allowedDataTypes);
}
protected JComponent createEditorPanel(DataTypeSelectionEditor dtEditor) {

View File

@ -29,6 +29,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.*;
import ghidra.util.data.DataTypeParser;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.exception.CancelledException;
/**
@ -68,14 +69,34 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// optional path to initially select in the data type chooser tree
private TreePath initiallySelectedTreePath;
public DataTypeSelectionEditor(ServiceProvider serviceProvider,
/**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Extra copies of data types that are
* already in the preferred DTM will be suppressed.
* @param serviceProvider {@link ServiceProvider}
* @param allowedDataTypes {@link AllowedDataTypes} option enum, controls what kind of
* data types that will be shown
*/
public DataTypeSelectionEditor(DataTypeManager dtm, ServiceProvider serviceProvider,
DataTypeParser.AllowedDataTypes allowedDataTypes) {
this(serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes);
this(dtm, serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes);
}
public DataTypeSelectionEditor(DataTypeManagerService service,
/**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Extra copies of data types that are
* already in the preferred DTM will be suppressed.
* @param service {@link DataTypeManagerService}
* @param allowedDataTypes {@link AllowedDataTypes} option enum, controls what kind of
* data types that will be shown
*/
public DataTypeSelectionEditor(DataTypeManager dtm, DataTypeManagerService service,
DataTypeParser.AllowedDataTypes allowedDataTypes) {
this.dataTypeManager = dtm;
if (service == null) {
throw new NullPointerException("DataTypeManagerService cannot be null");
}
@ -86,19 +107,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
init();
}
/**
* Sets the {@link DataTypeManager} to use when the chooser is forced to parse the given
* data type text to resolve the data type. If the users chooses a type, then this value
* is not used. Note that setting this value does not restrict the parser to just the
* given value, but rather the given value is the preferred manager and is thus searched
* first.
*
* @param dataTypeManager the preferred data type manager
*/
public void setPreferredDataTypeManager(DataTypeManager dataTypeManager) {
this.dataTypeManager = dataTypeManager;
}
/**
* Sets whether this editor should consumer Enter key presses
* @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean)
@ -116,7 +124,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
private void init() {
selectionField = createDropDownSelectionTextField(
new DataTypeDropDownSelectionDataModel(dataTypeManagerService));
new DataTypeDropDownSelectionDataModel(dataTypeManager, dataTypeManagerService));
selectionField.addCellEditorListener(new CellEditorListener() {
@Override
public void editingCanceled(ChangeEvent e) {

View File

@ -125,7 +125,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
@Override
protected DataTypeSelectionEditor createEditor(PluginTool pluginTool,
AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(pluginTool, allowedDataTypes) {
return new DataTypeSelectionEditor(null, pluginTool, allowedDataTypes) {
@Override
protected DropDownSelectionTextField<DataType> createDropDownSelectionTextField(
@ -1207,8 +1207,8 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
new DefaultHighlighter.DefaultHighlightPainter(Palette.YELLOW));
JPanel editorPanel = new JPanel(new BorderLayout());
DataTypeSelectionEditor editor = new DataTypeSelectionEditor(tool, AllowedDataTypes.ALL);
editor.setPreferredDataTypeManager(program.getDataTypeManager());
DataTypeSelectionEditor editor =
new DataTypeSelectionEditor(program.getDataTypeManager(), tool, AllowedDataTypes.ALL);
editorPanel.add(panelUpdateField, BorderLayout.SOUTH);
editorPanel.add(editor.getEditorComponent(), BorderLayout.NORTH);