Merge remote-tracking branch

'origin/GP-1984_Dan_RegisterAndWatchDataTypeSettings--SQUASHED'

Conflicts:
	Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
This commit is contained in:
Ryan Kurtz 2022-08-16 14:30:38 -04:00
commit e999a24433
18 changed files with 540 additions and 67 deletions

View file

@ -92,6 +92,12 @@
recorder, but they can still be populated by the user. Modifying the values of unknown recorder, but they can still be populated by the user. Modifying the values of unknown
registers cannot affect the target. Register sets are memorized per compiler specification.</P> registers cannot affect the target. Register sets are memorized per compiler specification.</P>
<H3><A name="type_settings"></A>Register Type Settings</H3>
<P>This action is available on the context menu when there is a single register selected with a
data type assigned. It permits the adjustment of that data type's settings, e.g., to display
decimal vs hexadecimal. The settings are saved to the data unit for the register.</P>
<H3><A name="enable_edits"></A>Enable Edits</H3> <H3><A name="enable_edits"></A>Enable Edits</H3>
<P>This toggle is a write protector for machine state. To modify register values, this toggle <P>This toggle is a write protector for machine state. To modify register values, this toggle

View file

@ -109,6 +109,12 @@
listing. That is, it attempts to apply the selected data type to the evaluated address, sizing listing. That is, it attempts to apply the selected data type to the evaluated address, sizing
it to the value's size.</P> it to the value's size.</P>
<H3><A name="type_settings"></A>Watch Type Settings</H3>
<P>This action is available on the context menu when there is a single watch selected with a
data type assigned. It permits the adjustment of that data type's settings, e.g., to display
decimal vs. hexadecimal.</P>
<H3><A name="select_addresses"></A>Select Range</H3> <H3><A name="select_addresses"></A>Select Range</H3>
<P>This action is available when there's an active trace, and at least one watch with memory <P>This action is available when there's an active trace, and at least one watch with memory

View file

@ -2385,4 +2385,12 @@ public interface DebuggerResources {
String DESCRIPTION_CHOOSE_MORE_PLATFORMS = String DESCRIPTION_CHOOSE_MORE_PLATFORMS =
"Choose from more platforms to use with the current trace"; "Choose from more platforms to use with the current trace";
String NAME_CLEAR_REGISTER_TYPE = "Clear Register Type";
String DESCRIPTION_CLEAR_REGISTER_TYPE = "Clear the register's data type";
String NAME_REGISTER_TYPE_SETTINGS = "Register Type Settings";
String DESCRIPTION_REGISTER_TYPE_SETTINGS = "Set the register's data type settings";
String NAME_WATCH_TYPE_SETTINGS = "Watch Type Settings";
String DESCRIPTION_WATCH_TYPE_SETTINGS = "Set the watch's data type settings";
} }

View file

@ -40,6 +40,7 @@ import docking.actions.PopupActionProvider;
import docking.widgets.table.*; import docking.widgets.table.*;
import docking.widgets.table.ColumnSortState.SortDirection; import docking.widgets.table.ColumnSortState.SortDirection;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.plugin.core.data.DataSettingsDialog;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerProvider; import ghidra.app.plugin.core.debug.gui.DebuggerProvider;
@ -53,18 +54,19 @@ import ghidra.base.widgets.table.DataTypeTableCellEditor;
import ghidra.dbg.error.DebuggerModelAccessException; import ghidra.dbg.error.DebuggerModelAccessException;
import ghidra.dbg.target.TargetRegisterBank; import ghidra.dbg.target.TargetRegisterBank;
import ghidra.dbg.target.TargetThread; import ghidra.dbg.target.TargetThread;
import ghidra.docking.settings.Settings;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.options.AutoOptions; import ghidra.framework.options.AutoOptions;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.options.annotation.*; import ghidra.framework.options.annotation.*;
import ghidra.framework.plugintool.AutoService; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeEncodeException; import ghidra.program.model.data.DataTypeEncodeException;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.Trace.*; import ghidra.trace.model.Trace.*;
@ -74,8 +76,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.Msg; import ghidra.util.*;
import ghidra.util.Swing;
import ghidra.util.data.DataTypeParser.AllowedDataTypes; import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -87,6 +88,50 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
implements DebuggerProvider, PopupActionProvider { implements DebuggerProvider, PopupActionProvider {
private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates"; private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates";
interface ClearRegisterType {
String NAME = DebuggerResources.NAME_CLEAR_REGISTER_TYPE;
String DESCRIPTION = DebuggerResources.DESCRIPTION_CLEAR_REGISTER_TYPE;
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION);
}
}
interface RegisterTypeSettings {
String NAME = DebuggerResources.NAME_REGISTER_TYPE_SETTINGS;
String DESCRIPTION = DebuggerResources.DESCRIPTION_REGISTER_TYPE_SETTINGS;
String HELP_ANCHOR = "type_settings";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
/**
* This only exists so that tests can access it
*/
protected static class RegisterDataSettingsDialog extends DataSettingsDialog {
public RegisterDataSettingsDialog(Data data) {
super(data);
}
@Override
protected Settings getSettings() {
return super.getSettings();
}
@Override
protected void okCallback() {
super.okCallback();
}
}
protected enum RegisterTableColumns protected enum RegisterTableColumns
implements EnumeratedTableColumn<RegisterTableColumns, RegisterRow> { implements EnumeratedTableColumn<RegisterTableColumns, RegisterRow> {
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, // FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, //
@ -429,7 +474,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
GhidraTable regsTable; GhidraTable regsTable;
RegistersTableModel regsTableModel = new RegistersTableModel(); RegistersTableModel regsTableModel = new RegistersTableModel();
private GhidraTableFilterPanel<RegisterRow> regsFilterPanel; GhidraTableFilterPanel<RegisterRow> regsFilterPanel;
Map<Register, RegisterRow> regMap = new HashMap<>(); Map<Register, RegisterRow> regMap = new HashMap<>();
private final DebuggerAvailableRegistersDialog availableRegsDialog; private final DebuggerAvailableRegistersDialog availableRegsDialog;
@ -438,6 +483,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
DockingAction actionCreateSnapshot; DockingAction actionCreateSnapshot;
ToggleDockingAction actionEnableEdits; ToggleDockingAction actionEnableEdits;
DockingAction actionClearDataType; DockingAction actionClearDataType;
DockingAction actionDataTypeSettings;
DebuggerRegisterActionContext myActionContext; DebuggerRegisterActionContext myActionContext;
AddressSetView viewKnown; AddressSetView viewKnown;
@ -618,11 +664,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
.onAction(c -> { .onAction(c -> {
}) })
.buildAndInstallLocal(this); .buildAndInstallLocal(this);
actionClearDataType = new ActionBuilder("Clear Register Type", plugin.getName()) actionClearDataType = ClearRegisterType.builder(plugin)
.enabledWhen(c -> current.getThread() != null) .enabledWhen(c -> current.getThread() != null)
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)) .keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0))
.onAction(c -> clearDataTypeActivated()) .onAction(c -> clearDataTypeActivated())
.buildAndInstallLocal(this); .buildAndInstallLocal(this);
actionDataTypeSettings = RegisterTypeSettings.builder(plugin)
.withContext(DebuggerRegisterActionContext.class)
.enabledWhen(this::contextHasSingleRegisterWithType)
.onAction(this::dataTypeSettingsActivated)
.buildAndInstallLocal(this);
} }
private void selectRegistersActivated() { private void selectRegistersActivated() {
@ -653,6 +704,22 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
row.setDataType(null); row.setDataType(null);
} }
private boolean contextHasSingleRegisterWithType(DebuggerRegisterActionContext ctx) {
return ctx.getSelected() != null && ctx.getSelected().getData() != null;
}
private void dataTypeSettingsActivated(DebuggerRegisterActionContext ctx) {
RegisterRow row = ctx.getSelected();
if (row == null) {
return;
}
Data data = row.getData();
if (data == null) {
return;
}
tool.showDialog(new RegisterDataSettingsDialog(data));
}
// TODO: "Refresh" action to flush cache and re-fetch selected registers // TODO: "Refresh" action to flush cache and re-fetch selected registers
@Override @Override

View file

@ -21,6 +21,7 @@ import java.util.Objects;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Data;
import ghidra.util.Msg; import ghidra.util.Msg;
public class RegisterRow { public class RegisterRow {
@ -84,6 +85,10 @@ public class RegisterRow {
return provider.getRegisterValue(register); return provider.getRegisterValue(register);
} }
public Data getData() {
return provider.getRegisterData(register);
}
public void setDataType(DataType dataType) { public void setDataType(DataType dataType) {
provider.writeRegisterDataType(register, dataType); provider.writeRegisterDataType(register, dataType);
} }

View file

@ -24,24 +24,23 @@ import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
@PluginInfo( // @PluginInfo(
shortDescription = "Debugger watches manager", // shortDescription = "Debugger watches manager",
description = "GUI to watch values of expressions", // description = "GUI to watch values of expressions",
category = PluginCategoryNames.DEBUGGER, // category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME, // packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED, // status = PluginStatus.RELEASED,
eventsConsumed = { eventsConsumed = {
TraceActivatedPluginEvent.class, // TraceActivatedPluginEvent.class,
}, // },
servicesRequired = { // servicesRequired = {
DebuggerModelService.class, // DebuggerModelService.class,
DebuggerTraceManagerService.class, // DebuggerTraceManagerService.class,
DataTypeManagerService.class, // For DataType selection field DataTypeManagerService.class, // For DataType selection field
} // })
)
public class DebuggerWatchesPlugin extends AbstractDebuggerPlugin { public class DebuggerWatchesPlugin extends AbstractDebuggerPlugin {
private DebuggerWatchesProvider provider; DebuggerWatchesProvider provider;
public DebuggerWatchesPlugin(PluginTool tool) { public DebuggerWatchesPlugin(PluginTool tool) {
super(tool); super(tool);

View file

@ -29,14 +29,18 @@ import javax.swing.*;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import org.jdom.Element;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.action.builder.ActionBuilder;
import docking.widgets.table.*; import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.core.data.AbstractSettingsDialog;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@ -47,14 +51,13 @@ import ghidra.app.services.*;
import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer; import ghidra.async.AsyncTimer;
import ghidra.base.widgets.table.DataTypeTableCellEditor; import ghidra.base.widgets.table.DataTypeTableCellEditor;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.*;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.options.annotation.AutoOptionDefined; import ghidra.framework.options.annotation.AutoOptionDefined;
import ghidra.framework.options.annotation.HelpInfo; import ghidra.framework.options.annotation.HelpInfo;
import ghidra.framework.plugintool.AutoService; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.pcode.exec.trace.TraceSleighUtils; import ghidra.pcode.exec.trace.TraceSleighUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -71,16 +74,64 @@ import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.trace.util.TraceAddressSpace; import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.Msg; import ghidra.util.*;
import ghidra.util.Swing;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.AbstractGColumnRenderer; import ghidra.util.table.column.AbstractGColumnRenderer;
public class DebuggerWatchesProvider extends ComponentProviderAdapter { public class DebuggerWatchesProvider extends ComponentProviderAdapter {
private static final String KEY_EXPRESSION_LIST = "expressionList"; private static final String KEY_ROW_COUNT = "rowCount";
private static final String KEY_TYPE_LIST = "typeList"; private static final String PREFIX_ROW = "row";
interface WatchTypeSettings {
String NAME = DebuggerResources.NAME_WATCH_TYPE_SETTINGS;
String DESCRIPTION = DebuggerResources.DESCRIPTION_WATCH_TYPE_SETTINGS;
String HELP_ANCHOR = "type_settings";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
protected static class WatchDataSettingsDialog extends AbstractSettingsDialog {
private final WatchRow row;
public WatchDataSettingsDialog(WatchRow row) {
super("Data Type Settings", row.getDataType().getSettingsDefinitions(),
row.getSettings());
this.row = row;
}
@Override
protected Settings getSettings() {
return super.getSettings();
}
@Override
protected void okCallback() {
super.okCallback();
}
@Override
protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (!settingsDefinition.supportsSuggestedValues()) {
return null;
}
return settingsDefinition.getSuggestedValues(row.getSettings());
}
@Override
protected void applySettings() throws CancelledException {
copySettings(getSettings(), row.getSettings(), getSettingsDefinitions());
row.settingsChanged();
}
}
protected enum WatchTableColumns implements EnumeratedTableColumn<WatchTableColumns, WatchRow> { protected enum WatchTableColumns implements EnumeratedTableColumn<WatchTableColumns, WatchRow> {
EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression), EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression),
@ -151,6 +202,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
} }
} }
protected static void copySettings(Settings src, Settings dst, SettingsDefinition[] defs) {
for (SettingsDefinition sd : defs) {
sd.copySetting(src, dst);
}
}
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) { protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) { if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false; return false;
@ -299,6 +356,7 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
DockingAction actionSelectAllReads; DockingAction actionSelectAllReads;
DockingAction actionAdd; DockingAction actionAdd;
DockingAction actionRemove; DockingAction actionRemove;
DockingAction actionDataTypeSettings;
DockingAction actionAddFromLocation; DockingAction actionAddFromLocation;
DockingAction actionAddFromRegister; DockingAction actionAddFromRegister;
@ -442,6 +500,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
.onAction(this::activatedRemove) .onAction(this::activatedRemove)
.buildAndInstallLocal(this); .buildAndInstallLocal(this);
actionDataTypeSettings = WatchTypeSettings.builder(plugin)
.withContext(DebuggerWatchActionContext.class)
.enabledWhen(this::selIsOneWithDataType)
.onAction(this::activatedDataTypeSettings)
.buildAndInstallLocal(this);
// Pop-up context actions // Pop-up context actions
actionAddFromLocation = WatchAction.builder(plugin) actionAddFromLocation = WatchAction.builder(plugin)
.withContext(ProgramLocationActionContext.class) .withContext(ProgramLocationActionContext.class)
@ -491,6 +555,11 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
return false; return false;
} }
protected boolean selIsOneWithDataType(DebuggerWatchActionContext ctx) {
WatchRow row = ctx.getWatchRow();
return row != null && row.getDataType() != null;
}
private void activatedApplyDataType(DebuggerWatchActionContext context) { private void activatedApplyDataType(DebuggerWatchActionContext context) {
if (current.getTrace() == null) { if (current.getTrace() == null) {
return; return;
@ -526,7 +595,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
UndoableTransaction.start(current.getTrace(), "Apply Watch Data Type")) { UndoableTransaction.start(current.getTrace(), "Apply Watch Data Type")) {
try { try {
listing.clearCodeUnits(row.getAddress(), row.getRange().getMaxAddress(), false); listing.clearCodeUnits(row.getAddress(), row.getRange().getMaxAddress(), false);
listing.createData(address, dataType, size); Data data = listing.createData(address, dataType, size);
copySettings(row.getSettings(), data, dataType.getSettingsDefinitions());
} }
catch (CodeUnitInsertionException e) { catch (CodeUnitInsertionException e) {
errs.add(address + " " + dataType + "(" + size + "): " + e.getMessage()); errs.add(address + " " + dataType + "(" + size + "): " + e.getMessage());
@ -579,6 +649,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
watchTableModel.deleteWith(context.getWatchRows()::contains); watchTableModel.deleteWith(context.getWatchRows()::contains);
} }
private void activatedDataTypeSettings(DebuggerWatchActionContext context) {
WatchRow row = context.getWatchRow();
if (row == null) {
return;
}
DataType type = row.getDataType();
if (type == null) {
return;
}
tool.showDialog(new WatchDataSettingsDialog(row));
}
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) { private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
if (someLoc == null) { if (someLoc == null) {
return null; return null;
@ -797,26 +879,30 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
public void writeConfigState(SaveState saveState) { public void writeConfigState(SaveState saveState) {
List<WatchRow> rows = List.copyOf(watchTableModel.getModelData()); List<WatchRow> rows = List.copyOf(watchTableModel.getModelData());
String[] expressions = rows.stream().map(WatchRow::getExpression).toArray(String[]::new); saveState.putInt(KEY_ROW_COUNT, rows.size());
String[] types = rows.stream().map(WatchRow::getTypePath).toArray(String[]::new); for (int i = 0; i < rows.size(); i++) {
saveState.putStrings(KEY_EXPRESSION_LIST, expressions); WatchRow row = rows.get(i);
saveState.putStrings(KEY_TYPE_LIST, types); String stateName = PREFIX_ROW + i;
SaveState rowState = new SaveState();
row.writeConfigState(rowState);
saveState.putXmlElement(stateName, rowState.saveToXml());
}
} }
public void readConfigState(SaveState saveState) { public void readConfigState(SaveState saveState) {
String[] expressions = saveState.getStrings(KEY_EXPRESSION_LIST, new String[] {}); int rowCount = saveState.getInt(KEY_ROW_COUNT, 0);
String[] types = saveState.getStrings(KEY_TYPE_LIST, new String[] {});
if (expressions.length != types.length) {
Msg.error(this, "Watch provider config error. Unequal number of expressions and types");
return;
}
int len = expressions.length;
List<WatchRow> rows = new ArrayList<>(); List<WatchRow> rows = new ArrayList<>();
for (int i = 0; i < len; i++) { for (int i = 0; i < rowCount; i++) {
WatchRow r = new WatchRow(this, expressions[i]); String stateName = PREFIX_ROW + i;
r.setTypePath(types[i]); Element rowElement = saveState.getXmlElement(stateName);
if (rowElement != null) {
WatchRow r = new WatchRow(this, "");
SaveState rowState = new SaveState(rowElement);
r.readConfigState(rowState);
rows.add(r); rows.add(r);
} }
}
watchTableModel.clear();
watchTableModel.addAll(rows); watchTableModel.addAll(rows);
} }

View file

@ -0,0 +1,93 @@
/* ###
* 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.debug.gui.watch;
import ghidra.docking.settings.*;
import ghidra.framework.options.SaveState;
import ghidra.program.model.data.TypeDefSettingsDefinition;
public class SavedSettings {
private final Settings settings;
private SaveState state;
public SavedSettings(Settings settings) {
this.settings = settings;
setState(state);
}
public void setState(SaveState state) {
if (state == null) {
state = new SaveState("Settings");
}
this.state = state;
}
public SaveState getState() {
return state;
}
public void write(SettingsDefinition[] definitions, Settings defaultSettings) {
for (SettingsDefinition sd : definitions) {
if (sd.hasSameValue(settings, defaultSettings)) {
continue;
}
if (sd instanceof BooleanSettingsDefinition bsd) {
state.putBoolean(sd.getStorageKey(), bsd.getValue(settings));
}
else if (sd instanceof EnumSettingsDefinition esd) {
state.putInt(sd.getStorageKey(), esd.getChoice(settings));
}
else if (sd instanceof NumberSettingsDefinition nsd) {
state.putLong(sd.getStorageKey(), nsd.getValue(settings));
}
else if (sd instanceof StringSettingsDefinition ssd) {
state.putString(sd.getStorageKey(), ssd.getValue(settings));
}
else if (sd instanceof TypeDefSettingsDefinition tdsd) {
// Toss this on the floor
}
else {
throw new AssertionError();
}
}
}
public void read(SettingsDefinition[] definitions, Settings defaultSettings) {
for (SettingsDefinition sd : definitions) {
if (!state.hasValue(sd.getStorageKey())) {
continue;
}
if (sd instanceof BooleanSettingsDefinition bsd) {
bsd.setValue(settings, state.getBoolean(sd.getStorageKey(), false));
}
else if (sd instanceof EnumSettingsDefinition esd) {
esd.setChoice(settings, state.getInt(sd.getStorageKey(), 0));
}
else if (sd instanceof NumberSettingsDefinition nsd) {
nsd.setValue(settings, state.getLong(sd.getStorageKey(), 0));
}
else if (sd instanceof StringSettingsDefinition ssd) {
ssd.setValue(settings, state.getString(sd.getStorageKey(), null));
}
else if (sd instanceof TypeDefSettingsDefinition tdsd) {
// Toss this on the floor
}
else {
throw new AssertionError();
}
}
}
}

View file

@ -27,7 +27,9 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.DebuggerStateEditingService; import ghidra.app.services.DebuggerStateEditingService;
import ghidra.app.services.DebuggerStateEditingService.StateEditor; import ghidra.app.services.DebuggerStateEditingService.StateEditor;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsImpl; import ghidra.docking.settings.SettingsImpl;
import ghidra.framework.options.SaveState;
import ghidra.pcode.exec.*; import ghidra.pcode.exec.*;
import ghidra.pcode.exec.trace.TraceBytesPcodeExecutorState; import ghidra.pcode.exec.trace.TraceBytesPcodeExecutorState;
import ghidra.pcode.exec.trace.TraceSleighUtils; import ghidra.pcode.exec.trace.TraceSleighUtils;
@ -51,6 +53,9 @@ import ghidra.util.*;
public class WatchRow { public class WatchRow {
public static final int TRUNCATE_BYTES_LENGTH = 64; public static final int TRUNCATE_BYTES_LENGTH = 64;
private static final String KEY_EXPRESSION = "expression";
private static final String KEY_DATA_TYPE = "dataType";
private static final String KEY_SETTINGS = "settings";
private final DebuggerWatchesProvider provider; private final DebuggerWatchesProvider provider;
private Trace trace; private Trace trace;
@ -63,6 +68,8 @@ public class WatchRow {
private String expression; private String expression;
private String typePath; private String typePath;
private DataType dataType; private DataType dataType;
private SettingsImpl settings = new SettingsImpl();
private SavedSettings savedSettings = new SavedSettings(settings);
private PcodeExpression compiled; private PcodeExpression compiled;
private TraceMemoryState state; private TraceMemoryState state;
@ -150,7 +157,7 @@ public class WatchRow {
return ""; return "";
} }
MemBuffer buffer = new ByteMemBufferImpl(address, value, language.isBigEndian()); MemBuffer buffer = new ByteMemBufferImpl(address, value, language.isBigEndian());
return dataType.getRepresentation(buffer, SettingsImpl.NO_SETTINGS, value.length); return dataType.getRepresentation(buffer, settings, value.length);
} }
// TODO: DataType settings // TODO: DataType settings
@ -296,19 +303,24 @@ public class WatchRow {
protected void updateType() { protected void updateType() {
dataType = null; dataType = null;
if (trace == null || typePath == null) { if (typePath == null) {
return; return;
} }
// Try from the trace first
if (trace != null) {
dataType = trace.getDataTypeManager().getDataType(typePath); dataType = trace.getDataTypeManager().getDataType(typePath);
if (dataType != null) { if (dataType != null) {
return; return;
} }
DataTypeManagerService dtms = provider.getTool().getService(DataTypeManagerService.class);
if (dtms == null) {
return;
} }
// Either we have no trace, or the trace doesn't have the type.
// Try built-ins
DataTypeManagerService dtms = provider.getTool().getService(DataTypeManagerService.class);
if (dtms != null) {
dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath); dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath);
} }
// We're out of things to try, let null be null
}
public void setTypePath(String typePath) { public void setTypePath(String typePath) {
this.typePath = typePath; this.typePath = typePath;
@ -325,12 +337,37 @@ public class WatchRow {
valueString = parseAsDataTypeStr(); valueString = parseAsDataTypeStr();
valueObj = parseAsDataTypeObj(); valueObj = parseAsDataTypeObj();
provider.contextChanged(); provider.contextChanged();
settings.setDefaultSettings(dataType == null ? null : dataType.getDefaultSettings());
if (dataType != null) {
savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
}
} }
public DataType getDataType() { public DataType getDataType() {
return dataType; return dataType;
} }
/**
* Get the row's (mutable) data type settings
*
* <p>
* After mutating these settings, the client must call {@link #settingsChanged()} to update the
* row's display and save state.
*
* @return the settings
*/
public Settings getSettings() {
return settings;
}
public void settingsChanged() {
if (dataType != null) {
savedSettings.write(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
}
valueString = parseAsDataTypeStr();
provider.watchTableModel.fireTableDataChanged();
}
public Address getAddress() { public Address getAddress() {
return address; return address;
} }
@ -552,4 +589,20 @@ public class WatchRow {
} }
return !Arrays.equals(value, prevValue); return !Arrays.equals(value, prevValue);
} }
protected void writeConfigState(SaveState saveState) {
saveState.putString(KEY_EXPRESSION, expression);
saveState.putString(KEY_DATA_TYPE, typePath);
saveState.putSaveState(KEY_SETTINGS, savedSettings.getState());
}
protected void readConfigState(SaveState saveState) {
setExpression(saveState.getString(KEY_EXPRESSION, ""));
setTypePath(saveState.getString(KEY_DATA_TYPE, null));
savedSettings.setState(saveState.getSaveState(KEY_SETTINGS));
if (dataType != null) {
savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
}
}
} }

View file

@ -33,11 +33,14 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec; import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec; import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterDataSettingsDialog;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterTableColumns; import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterTableColumns;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin; import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
import ghidra.app.services.DebuggerStateEditingService; import ghidra.app.services.DebuggerStateEditingService;
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode; import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.docking.settings.FormatSettingsDefinition;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
@ -460,6 +463,43 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertTypeEquals(PointerDataType.dataType, data.getDataType()); assertTypeEquals(PointerDataType.dataType, data.getDataType());
} }
@Test
public void testModifyTypeSettingsAffectsTrace() throws Exception {
traceManager.openTrace(tb.trace);
TraceThread thread = addThread();
try (UndoableTransaction tid = tb.startTransaction()) {
tb.exec(0, 0, thread, List.of("pc = 100;"));
}
traceManager.activateThread(thread);
waitForSwing();
RegisterRow row = findRegisterRow(pc);
row.setDataType(LongLongDataType.dataType);
waitForSwing();
DBTraceCodeRegisterSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
assertNotNull(regCode);
TraceData data = regCode.data().getForRegister(0L, pc);
assertTypeEquals(LongLongDataType.dataType, data.getDataType());
assertEquals("64h", row.getRepresentation());
registersProvider.regsFilterPanel.setSelectedItem(row);
waitForSwing();
performEnabledAction(registersProvider, registersProvider.actionDataTypeSettings, false);
RegisterDataSettingsDialog dialog =
waitForDialogComponent(RegisterDataSettingsDialog.class);
Settings settings = dialog.getSettings();
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
runSwing(() -> dialog.okCallback());
// The data is the settings. Wonderful :/
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(data));
assertEquals("100", row.getRepresentation());
}
@Test @Test
public void testModifySubRegTypeAffectsTrace() throws Exception { public void testModifySubRegTypeAffectsTrace() throws Exception {
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);

View file

@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.junit.*; import org.junit.*;
@ -33,15 +34,20 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
import ghidra.app.plugin.core.debug.gui.register.*; import ghidra.app.plugin.core.debug.gui.register.*;
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider.WatchDataSettingsDialog;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin; import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode; import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
import ghidra.dbg.model.TestTargetRegisterBankInThread; import ghidra.dbg.model.TestTargetRegisterBankInThread;
import ghidra.docking.settings.FormatSettingsDefinition;
import ghidra.docking.settings.Settings;
import ghidra.framework.options.SaveState;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Data;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
@ -173,6 +179,76 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(TraceRegisterUtils.rangeForRegister(r0), row.getRange()); assertEquals(TraceRegisterUtils.rangeForRegister(r0), row.getRange());
} }
@Test
public void testActionApplyDataType() {
setRegisterValues(thread);
WatchRow row = watchesProvider.addWatch("*:4 r0");
row.setDataType(LongDataType.dataType);
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
format.setChoice(row.getSettings(), FormatSettingsDefinition.DECIMAL);
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
watchesProvider.watchFilterPanel.setSelectedItem(row);
waitForSwing();
performEnabledAction(watchesProvider, watchesProvider.actionApplyDataType, true);
Data u400000 = tb.trace.getCodeManager().data().getAt(0, tb.addr(0x00400000));
assertTrue(LongDataType.dataType.isEquivalent(u400000.getDataType()));
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(u400000));
}
@Test
public void testWatchWithDataTypeSettings() {
setRegisterValues(thread);
performAction(watchesProvider.actionAdd);
WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
row.setExpression("r0");
row.setDataType(LongLongDataType.dataType);
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
waitForSwing();
assertEquals("0x400000", row.getRawValueString());
assertEquals("400000h", row.getValueString());
assertNoErr(row);
Settings settings = row.getSettings();
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
runSwing(() -> format.setChoice(settings, FormatSettingsDefinition.DECIMAL));
assertEquals("4194304", row.getValueString());
}
@Test
public void testActionDataTypeSettings() {
setRegisterValues(thread);
performAction(watchesProvider.actionAdd);
WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
row.setExpression("r0");
row.setDataType(LongLongDataType.dataType);
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
waitForSwing();
watchesProvider.watchFilterPanel.setSelectedItem(row);
waitForSwing();
performEnabledAction(watchesProvider, watchesProvider.actionDataTypeSettings, false);
WatchDataSettingsDialog dialog = waitForDialogComponent(WatchDataSettingsDialog.class);
Settings settings = dialog.getSettings();
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
runSwing(() -> dialog.okCallback());
assertEquals("4194304", row.getValueString());
}
@Test @Test
public void testConstantWatch() { public void testConstantWatch() {
setRegisterValues(thread); setRegisterValues(thread);
@ -677,4 +753,42 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(symbol, row.getSymbol()); assertEquals(symbol, row.getSymbol());
} }
@Test
public void testSaveConfigState() throws Throwable {
// Setup some state
WatchRow row0 = watchesProvider.addWatch("r0");
WatchRow row1 = watchesProvider.addWatch("*:4 r1");
row0.setDataType(LongLongDataType.dataType);
Settings settings = row0.getSettings();
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
row0.settingsChanged();
// Save the state
SaveState saveState = new SaveState();
watchesPlugin.writeConfigState(saveState);
// Change some things
row1.setDataType(Pointer64DataType.dataType);
WatchRow row2 = watchesProvider.addWatch("r2");
waitForSwing();
assertEquals(Set.of(row0, row1, row2),
Set.copyOf(watchesProvider.watchTableModel.getModelData()));
// Restore saved state
watchesPlugin.readConfigState(saveState);
waitForSwing();
// Assert the older state
Map<String, WatchRow> rows = watchesProvider.watchTableModel.getModelData()
.stream()
.collect(Collectors.toMap(r -> r.getExpression(), r -> r));
assertEquals(2, rows.size());
WatchRow rRow0 = rows.get("r0");
assertTrue(LongLongDataType.dataType.isEquivalent(rRow0.getDataType()));
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(rRow0.getSettings()));
}
} }

View file

@ -31,7 +31,6 @@ import ghidra.trace.database.map.*;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.database.*; import ghidra.util.database.*;
import ghidra.util.database.annot.*; import ghidra.util.database.annot.*;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
@ -224,11 +223,6 @@ public class DBTraceDataSettingsAdapter
dataFactory); dataFactory);
} }
@Override
public DBTraceDataSettingsSpace get(TraceAddressSpace space, boolean createIfAbsent) {
return (DBTraceDataSettingsSpace) super.get(space, createIfAbsent);
}
@Override @Override
public DBTraceDataSettingsSpace getForSpace(AddressSpace space, boolean createIfAbsent) { public DBTraceDataSettingsSpace getForSpace(AddressSpace space, boolean createIfAbsent) {
return (DBTraceDataSettingsSpace) super.getForSpace(space, createIfAbsent); return (DBTraceDataSettingsSpace) super.getForSpace(space, createIfAbsent);

View file

@ -231,7 +231,7 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
@Override @Override
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) { public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
return root.getSettingsSpace(createIfAbsent); return (DBTraceDataSettingsSpace) root.getSettingsSpace(createIfAbsent);
} }
@Override @Override

View file

@ -25,7 +25,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; import ghidra.trace.database.data.DBTraceDataSettingsOperations;
import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
@ -243,8 +243,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
} }
@Override @Override
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) { public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
return getTrace().getDataSettingsAdapter().get(space, createIfAbsent); return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
.get(space, createIfAbsent);
} }
@Override @Override

View file

@ -276,7 +276,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
@Override @Override
public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) { public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
return getTrace().getDataSettingsAdapter().get(this, createIfAbsent); return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
.get(this, createIfAbsent);
} }
@Override @Override

View file

@ -258,7 +258,7 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
* @param settingsDefinition string settings definition * @param settingsDefinition string settings definition
* @return suggested string value (may be empty array or null) * @return suggested string value (may be empty array or null)
*/ */
abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition); protected abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition);
/** /**
* Apply changes to settings. This method must be ov * Apply changes to settings. This method must be ov

View file

@ -336,7 +336,7 @@ public class DataSettingsDialog extends AbstractSettingsDialog {
} }
@Override @Override
String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) { protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (!settingsDefinition.supportsSuggestedValues()) { if (!settingsDefinition.supportsSuggestedValues()) {
return null; return null;
} }

View file

@ -140,7 +140,7 @@ public class DataTypeSettingsDialog extends AbstractSettingsDialog {
} }
@Override @Override
String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) { protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (settingsDefinition.supportsSuggestedValues()) { if (settingsDefinition.supportsSuggestedValues()) {
return settingsDefinition.getSuggestedValues(getOriginalSettings()); return settingsDefinition.getSuggestedValues(getOriginalSettings());
} }