Merge remote-tracking branch 'origin/GP-4232-dragonmacher-option-error-on-undo'

This commit is contained in:
Ryan Kurtz 2024-01-18 07:42:55 -05:00
commit 22844881cc
2 changed files with 49 additions and 34 deletions

View File

@ -15,16 +15,14 @@
*/
package ghidra.framework.options;
// Support for a PropertyEditor that uses text.
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.*;
import java.beans.PropertyEditor;
import javax.swing.JCheckBox;
/**
* An implementation of PropertyComponent that is represented as a text field.
* A basic editor for booleans.
*/
public class PropertyBoolean extends JCheckBox implements ItemListener {
@ -32,46 +30,32 @@ public class PropertyBoolean extends JCheckBox implements ItemListener {
private boolean notifyEditorOfChanges = true;
/**
* Constructor new PropertyText.
* @param pe bean property editor that is used to get the value
* to show in the text field
* Constructor
* @param pe bean property editor that is used to get the value to show in the text field
*/
public PropertyBoolean(PropertyEditor pe) {
super();
setSelected((Boolean) pe.getValue());
editor = pe;
setSelected((Boolean) pe.getValue());
addItemListener(this);
editor.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Object value = editor.getValue();
if ((value instanceof Boolean) && !value.equals(getText())) {
notifyEditorOfChanges = false;
try {
setSelected((Boolean) value);
}
finally {
notifyEditorOfChanges = true;
}
editor.addPropertyChangeListener(evt -> {
Object value = editor.getValue();
if ((value instanceof Boolean) && !value.equals(getText())) {
notifyEditorOfChanges = false;
try {
setSelected((Boolean) value);
}
finally {
notifyEditorOfChanges = true;
}
}
});
}
//----------------------------------------------------------------------
// Change listener methods.
@Override
public void itemStateChanged(ItemEvent e) {
if (notifyEditorOfChanges) {
try {
editor.setValue(isSelected() ? Boolean.TRUE : Boolean.FALSE);
}
catch (IllegalArgumentException ex) {
// Quietly ignore.
}
editor.setValue(isSelected() ? Boolean.TRUE : Boolean.FALSE);
}
}
}

View File

@ -18,6 +18,7 @@ package ghidra.framework.data;
import java.beans.PropertyEditor;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import db.*;
import ghidra.framework.options.*;
@ -173,9 +174,18 @@ class OptionsDB extends AbstractOptions {
}
synchronized void clearCache() {
for (Option option : valueMap.values()) {
DBOption dbOption = (DBOption) option;
Set<Entry<String, Option>> entries = valueMap.entrySet();
Iterator<Entry<String, Option>> it = entries.iterator();
while (it.hasNext()) {
Entry<String, Option> entry = it.next();
DBOption dbOption = (DBOption) entry.getValue();
dbOption.clearCache();
// remove any options that have disappeared during an undo operation
if (!dbOption.isValid()) {
it.remove();
}
}
}
@ -195,6 +205,7 @@ class OptionsDB extends AbstractOptions {
catch (IOException e) {
domainObj.dbError(e);
}
List<String> optionNames = new ArrayList<>(names);
Collections.sort(optionNames);
return optionNames;
@ -256,13 +267,32 @@ class OptionsDB extends AbstractOptions {
private Object value = null;
private boolean isCached = false;
// Once an option has its value set, it becomes 'registered'. This seems conceptually
// more like a 'has been used' concept, but it is the way things are currently coded. See
// Option.setCurrentValue(). This class is special in that undo/redo operations may cause
// option values to come and go from the database. If we leave an option around that has
// been removed, it causes errors in the option UI when the value cannot be read. We use
// this flag to know when an option was never registered. In that case, if the db record
// goes away, then we will remove this options when our cache is cleared.
private boolean isRegisteredOriginally;
protected DBOption(String name, OptionType type, String description, HelpLocation help,
Object defaultValue, boolean isRegistered, PropertyEditor editor) {
super(name, type, description, help, defaultValue, isRegistered, editor);
isRegisteredOriginally = isRegistered;
getCurrentValue(); // initialize our defaults
}
boolean isValid() {
// all options registered by clients should stick around (see note above)
if (isRegisteredOriginally) {
return true;
}
DBRecord rec = getPropertyRecord(getName());
return rec != null;
}
@Override
public Object getCurrentValue() {
if (!isCached) {
@ -280,7 +310,8 @@ class OptionsDB extends AbstractOptions {
value = optionType.convertStringToObject(rec.getString(VALUE_COL));
}
else {
Msg.info(this, "The type for '" + this.getName() + "' has changed! Using default value.");
Msg.info(this, "The type for '" + this.getName() +
"' has changed! Using default value.");
value = getDefaultValue();
}
}