Merge remote-tracking branch 'origin/GP-4719_ghidra1_StandaloneDTMUndoRedo--SQUASHED'

This commit is contained in:
Ryan Kurtz
2024-07-01 14:46:43 -04:00
64 changed files with 1681 additions and 897 deletions
@@ -899,4 +899,10 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
super.close();
objectManager.waitWbWorkers();
}
@Override
protected void domainObjectRestored() {
super.domainObjectRestored();
dataTypeManager.notifyRestored();
}
}
@@ -92,14 +92,24 @@ public abstract class CompEditorModel extends CompositeEditorModel {
throw new IllegalStateException(
"Can't apply edits without a data type or data type manager.");
}
int transactionID = originalDTM.startTransaction("Edit " + getCompositeName());
boolean originalDtExists = originalDTM.contains(originalDt);
boolean renamed = false;
if (originalDtExists) {
String origName = originalDt.getName();
String editName = getCompositeName();
renamed = !origName.equals(editName);
}
String action = originalDtExists ? "Edit" : "Create";
if (renamed) {
action += "/Rename";
}
String type = (originalDt instanceof Union) ? " Union " : " Structure ";
int transactionID = originalDTM.startTransaction(action + type + getCompositeName());
try {
if (originalDTM.contains(originalDt)) {
if (originalDtExists) {
// Update the original structure.
String origName = originalDt.getName();
String editName = getCompositeName();
if (!origName.equals(editName)) {
if (renamed) {
String editName = getCompositeName();
try {
originalDt.setName(editName);
}
@@ -545,20 +545,21 @@ public abstract class CompositeEditorPanel extends JPanel
}
}
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
public void dataTypeManagerRestored() {
DataTypeManager originalDTM = model.getOriginalDataTypeManager();
if (originalDTM == null) {
// editor unloaded
return;
}
boolean reload = true;
String objectType = "domain object";
if (domainObject instanceof Program) {
objectType = "program";
String objectType;
if (originalDTM instanceof ProgramBasedDataTypeManager) {
objectType = "Program";
}
else if (domainObject instanceof DataTypeArchive) {
objectType = "data type archive";
else {
objectType = "Archive";
}
String archiveName = originalDTM.getName();
DataType dt = originalDTM.getDataType(model.getCompositeID());
if (dt instanceof Composite) {
Composite composite = (Composite) dt;
@@ -570,10 +571,9 @@ public abstract class CompositeEditorPanel extends JPanel
Composite originalDt = model.getOriginalComposite();
if (originalDt == null) {
provider.show();
String info =
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
Msg.showWarn(this, this, "Program Restored", info);
String info = "The " + objectType + " \"" + archiveName + "\" has been restored.\n" +
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
Msg.showWarn(this, this, objectType + " Restored", info);
return;
}
else if (originalDt.isDeleted()) {
@@ -586,8 +586,8 @@ public abstract class CompositeEditorPanel extends JPanel
// The user has modified the structure so prompt for whether or
// not to reload the structure.
String question =
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
model.getCompositeName() + "\" may have changed outside the editor.\n" +
"Discard edits & reload the " + model.getTypeName() + "?";
String title = "Reload " + model.getTypeName() + " Editor?";
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
@@ -261,9 +261,8 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
return editorModel.hasChanges();
}
@Override
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
editorPanel.domainObjectRestored(domainObject);
public void dataTypeManagerRestored() {
editorPanel.dataTypeManagerRestored();
}
@Override
@@ -24,7 +24,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
/**
* The data type manager for original composite data type being edited.
* This is where the edited datatype will be written back to.
@@ -43,7 +43,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
public CompositeViewerDataTypeManager(String rootName, Composite originalComposite) {
super(rootName, originalComposite.getDataTypeManager().getDataOrganization());
this.originalComposite = originalComposite;
transactionID = startTransaction("");
transactionID = super.startTransaction("");
originalDTM = originalComposite.getDataTypeManager();
ProgramArchitecture arch = originalDTM.getProgramArchitecture();
@@ -68,11 +68,11 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
}
@Override
public void close() {
endTransaction(transactionID, true);
public void close() {
super.endTransaction(transactionID, true);
super.close();
}
/**
* Get the {@link DataTypeManager} associated with the original composite datatype being edited.
* @return original datatype manager
@@ -82,7 +82,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
}
@Override
public ArchiveType getType() {
public ArchiveType getType() {
return originalDTM.getType();
}
@@ -103,4 +103,33 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
return super.resolve(dataType, handler);
}
//
// Transaction support has been disabled since a single open transaction is maintained
// until this DTM is closed.
//
@SuppressWarnings("sync-override")
@Override
public int startTransaction(String description) {
// ignore - not yet supported
return 0;
}
@Override
public void endTransaction(int txId, boolean commit) {
// ignore - not yet supported
}
@SuppressWarnings("sync-override")
@Override
public boolean canUndo() {
return false;
}
@SuppressWarnings("sync-override")
@Override
public boolean canRedo() {
return false;
}
}
@@ -959,6 +959,16 @@ abstract class CompositeViewerModel extends AbstractTableModel
// Don't care.
}
@Override
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
// don't care
}
@Override
public void restored(DataTypeManager dataTypeManager) {
provider.dataTypeManagerRestored();
}
//=================================================================================================
// Helper methods for CategoryChangeListener methods.
//=================================================================================================
@@ -1354,8 +1364,4 @@ abstract class CompositeViewerModel extends AbstractTableModel
return viewComposite.isPackingEnabled();
}
@Override
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
// don't care
}
}
@@ -47,13 +47,6 @@ public interface EditorProvider {
*/
public DataTypeManager getDataTypeManager();
/**
* Notification that the data type manager domain object (program or data type archive) was
* restored.
* @param domainObject the program or data type archive that was restored.
*/
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
/**
* Return whether this editor is editing the data type with the given path.
* @param dtPath path of a data type
@@ -1264,7 +1264,8 @@ class StructureEditorModel extends CompEditorModel {
private DataType createDataTypeInOriginalDTM(StructureDataType structureDataType) {
boolean commit = false;
DataTypeManager originalDTM = getOriginalDataTypeManager();
int transactionID = originalDTM.startTransaction("Creating " + structureDataType.getName());
int transactionID =
originalDTM.startTransaction("Create structure " + structureDataType.getName());
try {
DataType addedDataType =
originalDTM.addDataType(structureDataType, DataTypeConflictHandler.DEFAULT_HANDLER);
@@ -290,7 +290,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin
DataTypeManagerDomainObject domainObject = (DataTypeManagerDomainObject) source;
provider.domainObjectRestored(domainObject);
dataTypePropertyManager.domainObjectRestored(domainObject);
editorManager.domainObjectRestored(domainObject);
// NOTE: each editor that cares about a restored DataTypeManager must establish
// a DataTypeManagerChangeListener and will be notified via the restored method.
}
}
else if (event.contains(DomainObjectEvent.RENAMED)) {
@@ -99,7 +99,7 @@ public class DataTypeSynchronizer {
}
private static void update(DataTypeManager refDTM, DataType sourceDT) {
int transactionID = refDTM.startTransaction("Update Datatype");
int transactionID = refDTM.startTransaction("Update Datatype " + sourceDT.getName());
try {
updateAssumingTransactionsOpen(refDTM, sourceDT);
}
@@ -184,8 +184,7 @@ public class DataTypeSynchronizer {
}
public void markSynchronized() {
int transactionID =
dataTypeManager.startTransaction("Clear dirty flag for data type manager.");
int transactionID = dataTypeManager.startTransaction("Clear Dirty Flag");
try {
sourceArchive.setDirtyFlag(false);
sourceArchive.setLastSyncTime(sourceDTM.getLastChangeTimeForMyManager());
@@ -457,8 +456,8 @@ public class DataTypeSynchronizer {
return;
}
int transactionID = dataTypeManager
.startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
int transactionID =
dataTypeManager.startTransaction("Sync '" + sourceArchive.getName() + "' data types");
try {
reSyncOutOfSyncInTimeOnlyDataTypes();
fixSyncForDifferingDataTypes();
@@ -525,7 +524,7 @@ public class DataTypeSynchronizer {
private void autoUpdateDataTypesThatHaveNoRealChanges(
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
int transactionID = dataTypeManager.startTransaction("auto sync datatypes");
int transactionID = dataTypeManager.startTransaction("Sync datatypes");
try {
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
dataTypeSyncInfo.syncTimes();
@@ -539,16 +538,16 @@ public class DataTypeSynchronizer {
}
}
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
ExceptionalConsumer<DataTypeSyncInfo, CancelledException> infoApplier,
Consumer<List<DataTypeSyncInfo>> handleOutOfSync,
boolean sourceRequiresTransaction) throws CancelledException {
Consumer<List<DataTypeSyncInfo>> handleOutOfSync, boolean sourceRequiresTransaction)
throws CancelledException {
if (sourceDTM == null) {
throw new RuntimeException("Source archive required");
}
int sourceTransactionId = sourceRequiresTransaction ?
sourceDTM.startTransaction(actionName) : 0;
int sourceTransactionId =
sourceRequiresTransaction ? sourceDTM.startTransaction(actionName) : 0;
int transactionID = dataTypeManager.startTransaction(actionName);
try {
for (DataTypeSyncInfo info : selectedList) {
@@ -181,6 +181,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
// FileEdit group
addLocalAction(new LockArchiveAction(plugin)); // Archive
addLocalAction(new UnlockArchiveAction(plugin)); // Archive
addLocalAction(new UndoArchiveTransactionAction(plugin)); // Archive
addLocalAction(new RedoArchiveTransactionAction(plugin)); // Archive
// Arch group
addLocalAction(new SetArchiveArchitectureAction(plugin)); // Archive
@@ -74,7 +74,7 @@ abstract class AbstractTypeDefAction extends DockingAction {
private DataType createNewTypeDef(Component parentComponent, TypeDef typedef,
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
DataType newdt = null;
int transactionID = dataTypeManager.startTransaction("Create Typedef");
int transactionID = dataTypeManager.startTransaction("Create Typedef " + typedef.getName());
try {
newdt = dataTypeManager.addDataType(typedef, plugin.getConflictHandler());
}
@@ -0,0 +1,150 @@
/* ###
* 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.datamgr.actions;
import javax.swing.tree.TreePath;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.StandAloneDataTypeManager;
public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAction {
private String actionName; // Undo / Redo
/**
* Construct Undo/Redo action
* @param actionName "Undo" or "Redo" action name
* @param plugin {@link DataTypeManagerPlugin}
*/
public AbstractUndoRedoArchiveTransactionAction(String actionName,
DataTypeManagerPlugin plugin) {
super(actionName + " Archive Change", plugin.getName());
this.actionName = actionName;
setPopupMenuData(getMenuData(null));
setEnabled(true);
}
private MenuData getMenuData(String txName) {
String name = actionName + " Change";
if (!StringUtils.isEmpty(txName)) {
name += ": " + txName;
}
return new MenuData(new String[] { name }, null, "FileEdit");
}
@Override
public boolean isAddToPopup(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return false;
}
TreePath[] selectionPaths = getSelectionPaths(context);
return getModifiableProjectOrFileDTM(selectionPaths) != null;
}
/**
* Determine if the corresponding undo/redo can be performed
* @param dtm archive datatype manager
* @return true if action can be performed on archive
*/
abstract protected boolean canExecute(StandAloneDataTypeManager dtm);
/**
* Determine the next undo/redo transaction name
* @param dtm archive datatype manager
* @return next undo/redo transaction name
*/
abstract protected String getNextName(StandAloneDataTypeManager dtm);
/**
* Execute the undo/redo operation on the specified archive datatype manager.
* @param dtm archive datatype manager
*/
abstract protected void execute(StandAloneDataTypeManager dtm);
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return false;
}
TreePath[] selectionPaths = getSelectionPaths(context);
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
if (dtm != null && canExecute(dtm)) {
setPopupMenuData(getMenuData(getNextName(dtm)));
return true;
}
setPopupMenuData(getMenuData(null));
return false;
}
@Override
public void actionPerformed(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return;
}
TreePath[] selectionPaths = getSelectionPaths(context);
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
if (dtm != null && canExecute(dtm)) {
execute(dtm);
}
}
private TreePath[] getSelectionPaths(ActionContext context) {
Object contextObject = context.getContextObject();
GTree gtree = (GTree) contextObject;
TreePath[] selectionPaths = gtree.getSelectionPaths();
return selectionPaths;
}
private StandAloneDataTypeManager getModifiableProjectOrFileDTM(TreePath[] selectionPaths) {
// only valid if single file or project archive node is selected
if (selectionPaths.length != 1) {
return null;
}
TreePath path = selectionPaths[0];
if (path.getPathCount() < 2) {
return null;
}
GTreeNode node = (GTreeNode) path.getPathComponent(1);
if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) {
return null;
}
ArchiveNode archiveNode = (ArchiveNode) node;
if (archiveNode.isModifiable()) {
DataTypeManager dtm = archiveNode.getArchive().getDataTypeManager();
if (dtm instanceof StandAloneDataTypeManager archiveDtm) {
return archiveDtm;
}
}
return null;
}
}
@@ -26,8 +26,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.program.model.data.Category;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.*;
import ghidra.util.InvalidNameException;
public class CreateCategoryAction extends DockingAction {
@@ -90,10 +89,10 @@ public class CreateCategoryAction extends DockingAction {
Archive archive = archiveNode.getArchive();
DataTypeManager dataTypeManager = archive.getDataTypeManager();
String newNodeName = null;
int transactionID = dataTypeManager.startTransaction("Create Category");
String newNodeName = getUniqueCategoryName(category);
String path = category.toString() + newNodeName;
int transactionID = dataTypeManager.startTransaction("Create " + path);
try {
newNodeName = getUniqueCategoryName(category);
category.createCategory(newNodeName);
}
catch (InvalidNameException ie) {
@@ -78,7 +78,8 @@ public class CreatePointerAction extends DockingAction {
private DataType createNewDataType(Component parentComponent, DataType dataType,
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
int transactionID = dataTypeManager.startTransaction("Create Typedef");
int transactionID =
dataTypeManager.startTransaction("Create Pointer " + dataType.getName());
try {
return dataTypeManager.addDataType(dataType, plugin.getConflictHandler());
}
@@ -79,7 +79,7 @@ public class Pack1DataTypeAction extends DockingAction {
boolean commit = false;
try {
// start a transaction
transactionID = dataTypeManager.startTransaction("pack of " + dataType.getName());
transactionID = dataTypeManager.startTransaction("Pack(1) " + dataType.getName());
packDataType(dataType);
commit = true;
}
@@ -29,7 +29,6 @@ import ghidra.util.Msg;
public class PackDataTypeAction extends DockingAction {
public PackDataTypeAction(DataTypeManagerPlugin plugin) {
super("Pack Data Type", plugin.getName());
setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit"));
@@ -100,20 +99,20 @@ public class PackDataTypeAction extends DockingAction {
private void alignDataType(DataType dataType, DataOrganization dataOrganization) {
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
if (dataTypeManager == null) {
Msg.error(this, "Can't align data type " + dataType.getName() +
" without a data type manager.");
Msg.error(this,
"Can't align data type " + dataType.getName() + " without a data type manager.");
return;
}
if (!(dataType instanceof Structure)) {
Msg.error(this, "Can't align data type " + dataType.getName() +
". It's not a structure.");
Msg.error(this,
"Can't align data type " + dataType.getName() + ". It's not a structure.");
return;
}
int transactionID = -1;
boolean commit = false;
try {
// start a transaction
transactionID = dataTypeManager.startTransaction("align " + dataType.getName());
transactionID = dataTypeManager.startTransaction("Pack " + dataType.getName());
((Structure) dataType).setPackingEnabled(true);
commit = true;
}
@@ -84,7 +84,7 @@ public class PackSizeDataTypeAction extends DockingAction {
try {
// start a transaction
transactionID =
dataTypeManager.startTransaction("pack(" + packSize + ") of " + dataType.getName());
dataTypeManager.startTransaction("Pack(" + packSize + ") " + dataType.getName());
packDataType(dataType, packSize);
commit = true;
}
@@ -0,0 +1,45 @@
/* ###
* 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.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.program.model.data.StandAloneDataTypeManager;
public class RedoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
public RedoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
super("Redo", plugin);
// Key-bind disabled by default to activation context concerns
//setKeyBindingData(new KeyBindingData("ctrl shift Z"));
setDescription("Redo last undone change made to data type archive");
}
@Override
protected boolean canExecute(StandAloneDataTypeManager dtm) {
return dtm.canRedo();
}
@Override
protected String getNextName(StandAloneDataTypeManager dtm) {
return dtm.getRedoName();
}
@Override
protected void execute(StandAloneDataTypeManager dtm) {
dtm.redo();
}
}
@@ -0,0 +1,45 @@
/* ###
* 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.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.program.model.data.StandAloneDataTypeManager;
public class UndoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
public UndoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
super("Undo", plugin);
// Key-bind disabled by default to activation context concerns
//setKeyBindingData(new KeyBindingData("ctrl Z"));
setDescription("Undo last change made to data type archive");
}
@Override
protected boolean canExecute(StandAloneDataTypeManager dtm) {
return dtm.canUndo();
}
@Override
protected String getNextName(StandAloneDataTypeManager dtm) {
return dtm.getUndoName();
}
@Override
protected void execute(StandAloneDataTypeManager dtm) {
dtm.undo();
}
}
@@ -282,7 +282,8 @@ public class AssociateDataTypeAction extends DockingAction {
}
boolean noErrors = false;
int tx = dtm.startTransaction("Create Category");
String path = archive.getName() + categoryPath;
int tx = dtm.startTransaction("Create " + path);
try {
category = dtm.createCategory(categoryPath);
noErrors = true;
@@ -131,7 +131,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
monitor.setMessage("Finding out-of-sync types");
List<DataTypeSyncInfo> outOfSynchDataTypes = synchronizer.findOutOfSynchDataTypes();
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, synchronizer.findOutOfSynchDataTypes());
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer,
synchronizer.findOutOfSynchDataTypes());
if (outOfSynchDataTypes.isEmpty()) {
showNoDataTypesToSyncMessage();
return;
@@ -194,20 +195,20 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
protected void processSelectedDataTypes(DataTypeSynchronizer synchronizer,
List<DataTypeSyncInfo> selectedList, List<DataTypeSyncInfo> outOfSynchDataTypes,
TaskMonitor monitor) throws CancelledException {
synchronizer.performBulkOperation(getName(), selectedList, info -> {
monitor.checkCancelled();
monitor.setMessage("Syncing " + info.getName());
applyOperation(info);
outOfSynchDataTypes.remove(info);
monitor.incrementProgress(1);
}, outOfSyncList -> {
// dataTypeChanged can cause other related data types to become updated
// and their times will appear out of sync. So clean up any that actually
// are the same.
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, outOfSyncList);
}, requiresArchiveOpenForEditing());
}
@@ -326,7 +327,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
int transactionID = dtm.startTransaction("Auto-sync data types");
int transactionID = dtm.startTransaction("Sync data types");
try {
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
dataTypeSyncInfo.syncTimes();
@@ -220,5 +220,10 @@ public class DataTypeIndexer {
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
markStale();
}
@Override
public void restored(DataTypeManager dataTypeManager) {
markStale();
}
}
}
@@ -640,7 +640,6 @@ public class DataTypeManagerHandler {
void dataTypeManagerChanged(FileArchive archive, DataTypeManager oldManager,
DataTypeManager newManager) {
oldManager.removeDataTypeManagerListener(listenerDelegate);
newManager.addDataTypeManagerListener(listenerDelegate);
dataTypeIndexer.removeDataTypeManager(oldManager);
@@ -1239,6 +1238,13 @@ public class DataTypeManagerHandler {
listener.programArchitectureChanged(dataTypeManager);
}
}
@Override
public void restored(DataTypeManager dataTypeManager) {
for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) {
listener.restored(dataTypeManager);
}
}
}
/**
@@ -1412,8 +1418,7 @@ public class DataTypeManagerHandler {
}
private DataTreeDialog getSaveDialog() {
DataTreeDialog dialog =
new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
ActionListener listener = event -> {
DomainFolder folder = dialog.getDomainFolder();
@@ -300,6 +300,12 @@ public class FileArchive implements Archive {
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
setChanged(true);
}
@Override
public void restored(DataTypeManager dataTypeManager) {
archiveManager.dataTypeManagerChanged(FileArchive.this, dataTypeManager,
dataTypeManager);
}
}
@Override
@@ -29,7 +29,7 @@ public class ProjectArchive implements DomainFileArchive {
private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.project.closed");
private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.project.open");
private DataTypeArchive dataTypeArchive;
private DomainFile sourceDomainFile;
private DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set
@@ -67,6 +67,7 @@ public class ProjectArchive implements DomainFileArchive {
return -1; // Project Archives appear between the ProgramArchive and FileArchives.
}
@Override
public boolean hasExclusiveAccess() {
return dataTypeArchive.hasExclusiveAccess();
}
@@ -202,5 +203,10 @@ public class ProjectArchive implements DomainFileArchive {
public void programArchitectureChanged(DataTypeManager dtm) {
fireStateChanged();
}
@Override
public void restored(DataTypeManager dtm) {
fireStateChanged();
}
}
}
@@ -390,34 +390,6 @@ public class DataTypeEditorManager implements EditorListener {
return false;
}
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
// Create a copy of the list since restore may remove an editor from the original list.
ArrayList<EditorProvider> list = new ArrayList<>(editorList);
// notify the editors
for (EditorProvider editor : list) {
DataTypeManager dataTypeManager = editor.getDataTypeManager();
DataTypeManager programDataTypeManager = domainObject.getDataTypeManager();
if (dataTypeManager == programDataTypeManager) {
/*
It is not clear why this check was added. It seem reasonable to always let the
editor know about the event. With this code enabled, editors with new, unsaved
types will be closed.
DataTypePath dtPath = editor.getDtPath();
CategoryPath categoryPath = dtPath.getCategoryPath();
String name = dtPath.getDataTypeName();
DataType dataType = programDataTypeManager.getDataType(categoryPath, name);
if (dataType == null || dataType.isDeleted()) {
dismissEditor(editor);
continue;
}
*/
editor.domainObjectRestored(domainObject);
}
}
}
/**
* If the specified data type is being edited for the indicated category, this gets that editor.
* @param dataType the data type
@@ -36,6 +36,7 @@ import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener;
import generic.theme.Gui;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.DataTypeArchive;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
@@ -120,30 +121,40 @@ class EnumEditorPanel extends JPanel {
});
}
void domainObjectRestored(DataTypeManagerDomainObject domainObject, EnumDataType enuum) {
void domainObjectRestored(EnumDataType enuum, boolean exists) {
stopCellEditing();
this.originalEnumDT = enuum;
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
DataTypeManager objectDataTypeManager = domainObject.getDataTypeManager();
DataTypeManager providerDataTypeManager = provider.getDataTypeManager();
if (objectDataTypeManager != providerDataTypeManager) {
return; // The editor isn't associated with the restored domain object.
}
DataTypeManager enumDtMgr = enuum.getDataTypeManager();
String objectType = "domain object";
if (domainObject instanceof Program) {
if (enumDtMgr instanceof ProgramBasedDataTypeManager) {
objectType = "program";
}
else if (domainObject instanceof DataTypeArchive) {
else {
objectType = "data type archive";
}
String archiveName = enumDtMgr.getName();
this.originalEnumDT = enuum;
if (tableModel.hasChanges()) {
if (!exists) {
if (OptionDialog.showOptionNoCancelDialog(this, "Close Enum Editor?",
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
enuum.getDisplayName() + "\" may no longer exist outside the editor.\n" +
"Do you want to close editor?",
"Close", "Continue Edit",
OptionDialog.WARNING_MESSAGE) == OptionDialog.OPTION_ONE) {
provider.dispose();
}
else {
provider.stateChanged(null);
}
return;
}
if (exists && tableModel.hasChanges()) {
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, "Reload Enum Editor?",
"The " + objectType + " \"" + objectDataTypeManager.getName() +
"\" has been restored.\n" + "\"" + tableModel.getEnum().getDisplayName() +
"\" may have changed outside this editor.\n" +
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
enuum.getDisplayName() + "\" may have changed outside this editor.\n" +
"Do you want to discard edits and reload the Enum?") == OptionDialog.OPTION_TWO) {
// 'No'; do not discard
@@ -153,6 +164,7 @@ class EnumEditorPanel extends JPanel {
}
// reload the enum
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
setFieldInfo(editedEnumDT);
tableModel.setEnum(editedEnumDT, false);
}
@@ -107,6 +107,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
}
originalCategoryPath = categoryPath;
originalEnum = enumDT;
originalEnumName = enumDT.getDisplayName();
dataTypeManager = enumDTM;
@@ -213,25 +214,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
return editorPanel.needsSave();
}
@Override
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
if (originalEnumID == -1) {
return;
}
Enum enuum = (Enum) dataTypeManager.getDataType(originalEnumID);
if (enuum != null) {
EnumDataType dt = (EnumDataType) enuum.copy(dataTypeManager);
originalEnumName = dt.getDisplayName();
updateTitle(dt);
Category category = dataTypeManager.getCategory(enuum.getCategoryPath());
originalCategoryPath = category.getCategoryPath();
editorPanel.domainObjectRestored(domainObject, dt);
}
tool.setStatusInfo("");
}
@Override
public boolean isTransient() {
return true;
@@ -348,11 +330,21 @@ public class EnumEditorProvider extends ComponentProviderAdapter
setStatusMessage("Empty enum is not allowed");
return false;
}
int txID = startTransaction();
boolean originalDtExists = dataTypeManager.contains(originalEnum);
boolean renamed = false;
if (originalDtExists) {
String editorName = editorPanel.getEnumName().trim();
renamed = !originalEnumName.equals(editorName);
}
String action = originalDtExists ? "Edit" : "Create";
if (renamed) {
action += "/Rename";
}
int txID = dataTypeManager.startTransaction(action + " Enum " + editedEnum.getName());
try {
DataTypeManager dtm = editedEnum.getDataTypeManager();
boolean userSaved = resolveEquateConflicts(editedEnum, dtm);
boolean userSaved = resolveEquateConflicts(editedEnum);
if (!userSaved) {
return false;
}
@@ -364,11 +356,12 @@ public class EnumEditorProvider extends ComponentProviderAdapter
newEnuum.replaceWith(editedEnum);
originalEnum = newEnuum;
originalEnumID = dataTypeManager.getID(newEnuum);
editorPanel.setEnum((EnumDataType) newEnuum.copy(dataTypeManager));
applyAction.setEnabled(hasChanges());
}
finally {
endTransaction(txID);
dataTypeManager.endTransaction(txID, true);
}
return true;
}
@@ -381,10 +374,9 @@ public class EnumEditorProvider extends ComponentProviderAdapter
/**
* Checks to see if the new changes to the enum will affect equates based off of it.
* @param editedEnum the enum to check for conflicts with
* @param dtm the data type manager that this enum lies within
* @return true if the enum should save its changes; otherwise, false
*/
private boolean resolveEquateConflicts(Enum editedEnum, DataTypeManager dtm) {
private boolean resolveEquateConflicts(Enum editedEnum) {
Program program = plugin.getProgram();
if (program == null) {
@@ -500,14 +492,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
}
}
private int startTransaction() {
return dataTypeManager.startTransaction("Edit Enum");
}
private void endTransaction(int transID) {
dataTypeManager.endTransaction(transID, true);
}
/**
* Prompts the user if the editor has unsaved changes. Saves the changes if
* the user indicates to do so.
@@ -692,6 +676,36 @@ public class EnumEditorProvider extends ComponentProviderAdapter
dispose();
}
@Override
public void restored(DataTypeManager dtm) {
if (originalEnumID <= 0) {
return;
}
DataTypeManager originalDTM = originalEnum.getDataTypeManager();
DataType dt = originalDTM.getDataType(originalEnumID);
boolean exists = false;
if (dt instanceof Enum) {
originalEnum = (Enum) dt;
exists = true;
}
else {
// original enum no longer exists
originalEnumID = -1;
EnumDataType enuum = editorPanel.getEnum();
originalEnum = new EnumDataType(enuum.getCategoryPath(), enuum.getName(),
enuum.getLength(), originalDTM);
}
originalEnumName = originalEnum.getDisplayName();
updateTitle(originalEnum);
originalCategoryPath = originalEnum.getCategoryPath();
editorPanel.domainObjectRestored((EnumDataType) originalEnum.copy(originalDTM), exists);
tool.setStatusInfo("");
}
private boolean isMyCategory(DataTypePath path) {
CategoryPath parentPath = path.getCategoryPath();
return parentPath.equals(originalCategoryPath);
@@ -480,5 +480,13 @@ public class ArchiveNode extends CategoryNode {
unloadChildren();
nodeChangedUpdater.update();
}
@Override
public void restored(DataTypeManager manager) {
// need to force all cached datatype tooltips to be cleared
// due to potential changes (e.g., undo/redo)
unloadChildren();
nodeChangedUpdater.update();
}
}
}
@@ -253,18 +253,20 @@ public class CategoryNode extends DataTypeTreeNode {
@Override
public void valueChanged(Object newValue) {
int transactionID = category.getDataTypeManager().startTransaction("rename");
String newName = newValue.toString();
int transactionID =
category.getDataTypeManager().startTransaction("Rename Category " + newName);
try {
category.setName(newValue.toString());
category.setName(newName);
}
catch (DuplicateNameException e) {
Msg.showError(getClass(), null, "Rename Failed",
"Category by the name " + newValue + " already exists in this category.");
"Category by the name " + newName + " already exists in this category.");
}
catch (InvalidNameException exc) {
String msg = exc.getMessage();
if (msg == null) {
msg = "Invalid name specified: " + newValue;
msg = "Invalid name specified: " + newName;
}
Msg.showError(getClass(), null, "Invalid name specified", exc.getMessage());
}
@@ -140,7 +140,7 @@ public class DataTypeNode extends DataTypeTreeNode {
return;
}
int transactionID = dataType.getDataTypeManager().startTransaction("rename");
int transactionID = dataType.getDataTypeManager().startTransaction("Rename DataType");
try {
dataType.setName(newName);
@@ -182,7 +182,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
DataTypeManager newDtm = createLayeredDataTypeManager();
int transactionId = newDtm.startTransaction("add datatypes");
int transactionId = newDtm.startTransaction("Add Datatypes");
try {
Iterator<DataType> allDataTypes = dataTypeManager.getAllDataTypes();
while (allDataTypes.hasNext()) {
@@ -343,7 +343,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
return;
}
int transactionID = dataTypeManager.startTransaction("Add dataType");
int transactionID = dataTypeManager.startTransaction("Add " + dt.getName());
try {
DataType resolvedDt = dataTypeManager.resolve(dt, null);
model.add(resolvedDt);
@@ -354,7 +354,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
}
private void removeDataType(DataType dt) {
int transactionID = dataTypeManager.startTransaction("Remove dataType");
int transactionID = dataTypeManager.startTransaction("Remove " + dt.getName());
try {
model.removeAll(dt);
@@ -24,7 +24,8 @@ import javax.swing.*;
import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.*;
import ghidra.util.exception.UsrException;
@@ -273,15 +274,9 @@ public class StackEditorPanel extends CompositeEditorPanel {
}
@Override
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
public void dataTypeManagerRestored() {
boolean reload = true;
String objectType = "domain object";
if (domainObject instanceof Program) {
objectType = "program";
}
else if (domainObject instanceof DataTypeArchive) {
objectType = "data type archive";
}
String objectType = "program";
DataTypeManager dtm = ((StackEditorModel) model).getOriginalDataTypeManager();
Composite originalDt = ((StackEditorModel) model).getOriginalComposite();
if (originalDt instanceof StackFrameDataType) {
@@ -306,8 +301,8 @@ public class StackEditorPanel extends CompositeEditorPanel {
// The user has modified the structure so prompt for whether or
// not to reload the structure.
String question =
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
"The " + objectType + " \"" + dtm.getName() + "\" has been restored.\n" + "\"" +
model.getCompositeName() + "\" may have changed outside the editor.\n" +
"Discard edits & reload the " + name + " Editor?";
String title = "Reload " + name + " Editor?";
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
@@ -23,7 +23,8 @@ import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataTypePath;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
@@ -144,12 +145,6 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
return actionMgr.getAllActions();
}
@Override
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
refreshName();
editorPanel.domainObjectRestored(domainObject);
}
private void refreshName() {
StackFrameDataType origDt = (StackFrameDataType) stackModel.getOriginalComposite();
StackFrameDataType viewDt = stackModel.getViewComposite();
@@ -187,11 +182,9 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
DomainObjectChangeRecord rec = event.getChangeRecord(i);
EventType eventType = rec.getEventType();
if (eventType == DomainObjectEvent.RESTORED) {
Object source = event.getSource();
if (source instanceof Program) {
Program restoredProgram = (Program) source;
domainObjectRestored(restoredProgram);
}
refreshName();
// NOTE: editorPanel should be notified of restored datatype manager via the
// CompositeViewerModel's DataTypeManagerChangeListener restored method
return;
}
if (eventType instanceof ProgramEvent type) {
@@ -460,7 +460,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return result;
}
/**
* Returns the golang version
* @return {@link GoVer}
@@ -842,9 +841,8 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
// gdt data base. This method only leaves the target gdt filename + ".step1" in the db.
File tmpGDTFile = new File(gdtFile.getParentFile(), gdtFile.getName() + ".step1.gdt");
FileDataTypeManager tmpFdtm = FileDataTypeManager.createFileArchive(tmpGDTFile);
int tx = -1;
int tx = tmpFdtm.startTransaction("Import");
try {
tx = tmpFdtm.startTransaction("Import");
tmpFdtm.addDataTypes(registeredStructDTs, DataTypeConflictHandler.DEFAULT_HANDLER,
monitor);
if (runtimeFuncSnapshot) {
@@ -879,17 +877,14 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
Msg.error(this, "Error when exporting types to file: %s".formatted(gdtFile), e);
}
finally {
if (tx != -1) {
tmpFdtm.endTransaction(tx, true);
}
tmpFdtm.endTransaction(tx, true);
}
tmpFdtm.save();
FileDataTypeManager fdtm = FileDataTypeManager.createFileArchive(gdtFile);
tx = -1;
tx = fdtm.startTransaction("Import");
try {
tx = fdtm.startTransaction("Import");
tmpFdtm.getAllDataTypes()
.forEachRemaining(
dt -> fdtm.addDataType(dt, DataTypeConflictHandler.DEFAULT_HANDLER));
@@ -898,9 +893,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
}
}
finally {
if (tx != -1) {
fdtm.endTransaction(tx, true);
}
fdtm.endTransaction(tx, true);
}
fdtm.save();
@@ -926,7 +919,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return existingDT;
}
private List<DataType> createBootstrapFuncDefs(DataTypeManager destDTM, CategoryPath destCP,
TaskMonitor monitor) throws CancelledException {
List<Function> funcs = getAllFunctions().stream()
@@ -959,7 +951,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return results;
}
private void moveAllDataTypesTo(DataTypeManager dtm, CategoryPath srcCP, CategoryPath destCP)
throws DuplicateNameException, DataTypeDependencyException, InvalidNameException {
Category srcCat = dtm.getCategory(srcCP);
@@ -1182,7 +1173,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
.map(Entry::getKey)
.collect(toSet());
typeDupCount.clear();
for (GoType goType : goTypes.values()) {
String typeName = goType.getNameWithPackageString();
if (dupedTypeNames.contains(typeName)) {
@@ -1270,10 +1261,11 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
StructureContext<T> structContext = getStructureContextOfInstance(structInstance);
String fallbackName = defaultValue;
fallbackName = fallbackName == null && structContext != null
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
structContext.getStructureStart())
: "invalid_object";
fallbackName =
fallbackName == null && structContext != null
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
structContext.getStructureStart())
: "invalid_object";
return GoName.createFakeInstance(fallbackName);
}
@@ -1296,7 +1288,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return "unknown_type_%x".formatted(offset);
}
/**
* Returns the {@link GoType} corresponding to an offset that is relative to the controlling
* GoModuledata's typesOffset.
@@ -1441,15 +1432,13 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
private AddressRange getPclntabSearchRange() {
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "rdata");
return memBlock != null
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
: null;
}
private AddressRange getModuledataSearchRange() {
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "data");
return memBlock != null
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
: null;
}
@@ -1489,7 +1478,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return result;
}
public Symbol getGoSymbol(String symbolName) {
return getGoSymbol(program, symbolName);
}
@@ -56,7 +56,7 @@ public class DataTypeCleaner implements Closeable {
this.targetDtm = targetDtm;
this.retainExistingComposites = retainExistingComposites;
this.cleanerDtm = new StandAloneDataTypeManager("CleanerDTM");
txId = cleanerDtm.startTransaction("CleanerTx");
txId = cleanerDtm.startTransaction("Clean Datatypes");
ProgramArchitecture arch = targetDtm.getProgramArchitecture();
if (arch != null) {
@@ -40,7 +40,6 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
import ghidra.app.plugin.core.stackeditor.StackEditorModel;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.framework.model.*;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginException;
@@ -488,20 +487,6 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration
program.endTransaction(txId, saveChanges);
}
protected class RestoreListener implements DomainObjectListener {
@Override
public void domainObjectChanged(DomainObjectChangedEvent event) {
if (event.contains(DomainObjectEvent.RESTORED)) {
Object source = event.getSource();
if (source instanceof DataTypeManagerDomainObject) {
DataTypeManagerDomainObject restoredDomainObject =
(DataTypeManagerDomainObject) source;
provider.domainObjectRestored(restoredDomainObject);
}
}
}
}
protected class StatusListener extends CompositeEditorModelAdapter {
String status = null;
boolean beep = false;
@@ -108,11 +108,9 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
// Test Undo / Redo of program.
@Test
public void testModifiedDtAndProgramRestored() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
init(complexStructure, pgmTestCat, false);
program.addListener(restoreListener);
// Change the structure
runSwingLater(() -> {
@@ -165,7 +163,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
@@ -173,7 +170,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
// This should close the edit session.
@Test
public void testProgramRestoreRemovesEditedDt() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
Structure s1 = new StructureDataType("s1", 0);
@@ -197,7 +193,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
final Structure myS1Structure = s1Struct;
init(myS1Structure, pgmTestCat, false);
program.addListener(restoreListener);
// Change the structure.
runSwingLater(() -> {
@@ -232,7 +227,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
@@ -240,7 +234,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
// program so it goes away. This should close the edit session.
@Test
public void testProgramRestoreRemovesEditedDtComp() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
Structure s1 = new StructureDataType("s1", 0);
@@ -268,7 +261,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
assertTrue(s2.isEquivalent(myS2Structure));
init(myS2Structure, pgmTestCat, false);
program.addListener(restoreListener);
// Change the structure.
runSwing(() -> {
@@ -303,7 +295,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
@@ -311,14 +302,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
// so it goes away. The editor stays since the structure existed previously, but editor reloads.
@Test
public void testProgramRestoreRemovesEditedComponentDtYes() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
Structure myStruct = new StructureDataType("myStruct", 0);
myStruct.add(new WordDataType());
init(emptyStructure, pgmTestCat, false);
program.addListener(restoreListener);
// Add the data type so that we can undo its add.
boolean commit = true;
@@ -371,7 +360,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
@@ -379,14 +367,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
// so it goes away. The editor stays since the structure existed previously, but doesn't reload.
@Test
public void testProgramRestoreRemovesEditedComponentDtNo() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
Structure myStruct = new StructureDataType("myStruct", 0);
myStruct.add(new WordDataType());
init(emptyStructure, pgmTestCat, false);
program.addListener(restoreListener);
// Add the data type so that we can undo its add.
boolean commit = true;
@@ -438,45 +424,37 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
// Test Undo / Redo of program.
@Test
public void testUnModifiedDtAndProgramRestored() throws Exception {
RestoreListener restoreListener = new RestoreListener();
try {
init(complexStructure, pgmTestCat, false);
program.addListener(restoreListener);
init(complexStructure, pgmTestCat, false);
// Change the structure
runSwingLater(() -> {
getTable().requestFocus();
setSelection(new int[] { 4, 5 });
deleteAction.actionPerformed(new DefaultActionContext());
try {
model.add(new WordDataType());
}
catch (UsrException e) {
Assert.fail(e.getMessage());
}
});
waitForSwing();
assertFalse(complexStructure.isEquivalent(model.viewComposite));
// Apply the changes
invoke(applyAction);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
// Undo the apply
undo(program);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
// Redo the apply
redo(program);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
}
finally {
program.removeListener(restoreListener);
}
// Change the structure
runSwingLater(() -> {
getTable().requestFocus();
setSelection(new int[] { 4, 5 });
deleteAction.actionPerformed(new DefaultActionContext());
try {
model.add(new WordDataType());
}
catch (UsrException e) {
Assert.fail(e.getMessage());
}
});
waitForSwing();
assertFalse(complexStructure.isEquivalent(model.viewComposite));
// Apply the changes
invoke(applyAction);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
// Undo the apply
undo(program);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
// Redo the apply
redo(program);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
}
@Test
@@ -31,8 +31,7 @@ import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.UsrException;
public class StructureEditorUnlockedActions5Test
extends AbstractStructureEditorTest {
public class StructureEditorUnlockedActions5Test extends AbstractStructureEditorTest {
@Test
public void testApplyDuplicateName() throws Exception {
@@ -614,14 +613,14 @@ public class StructureEditorUnlockedActions5Test
undo(program, false);
program.flushEvents();
waitForSwing();
runSwing(() -> provider.domainObjectRestored(program), true);
runSwing(() -> provider.dataTypeManagerRestored(), true);
waitForSwing();
assertEquals("myStruct", model.getCompositeName());
redo(program, false);
program.flushEvents();
waitForSwing();
runSwing(() -> provider.domainObjectRestored(program), true);
runSwing(() -> provider.dataTypeManagerRestored(), true);
waitForSwing();
assertEquals("myStruct2", model.getCompositeName());
@@ -83,11 +83,9 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
// Test Undo / Redo of program.
@Test
public void testModifiedDtAndProgramRestored() throws Exception {
RestoreListener restoreListener = new RestoreListener();
Window dialog;
try {
init(complexUnion, pgmTestCat, false);
program.addListener(restoreListener);
// Change the union.
Swing.runLater(() -> {
@@ -138,47 +136,39 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
}
finally {
dialog = null;
program.removeListener(restoreListener);
}
}
// Test Undo / Redo of program.
@Test
public void testUnModifiedDtAndProgramRestored() throws Exception {
RestoreListener restoreListener = new RestoreListener();
try {
init(complexUnion, pgmTestCat, false);
program.addListener(restoreListener);
init(complexUnion, pgmTestCat, false);
// Change the union.
Swing.runLater(() -> {
delete(4, 5);
try {
model.add(new WordDataType());
}
catch (UsrException e) {
Assert.fail(e.getMessage());
}
});
// Change the union.
Swing.runLater(() -> {
delete(4, 5);
try {
model.add(new WordDataType());
}
catch (UsrException e) {
Assert.fail(e.getMessage());
}
});
waitForTasks();
assertFalse(complexUnion.isEquivalent(model.viewComposite));
waitForTasks();
assertFalse(complexUnion.isEquivalent(model.viewComposite));
// Apply the changes
invoke(applyAction);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Apply the changes
invoke(applyAction);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Undo the apply
undo(program);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Undo the apply
undo(program);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Redo the apply
redo(program);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
}
finally {
program.removeListener(restoreListener);
}
// Redo the apply
redo(program);
assertTrue(complexUnion.isEquivalent(model.viewComposite));
}
@Test
@@ -0,0 +1,345 @@
/* ###
* 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.datamgr.editor;
import static org.junit.Assert.*;
import java.awt.*;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.*;
import org.junit.*;
import docking.DefaultActionContext;
import docking.action.DockingActionIf;
import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Program;
import ghidra.test.*;
/**
* {@link AbstractEnumEditorUndoRedoTest} contains tests which should be applied to the various
* {@link DataTypeManager} implementations which are responsible for setting {@code dtm} during
* the setUp phase.
*/
public abstract class AbstractEnumEditorUndoRedoTest extends AbstractGhidraHeadedIntegrationTest {
protected Program program;
protected DataTypeManagerPlugin plugin;
protected PluginTool tool;
protected TestEnv env;
protected DataTypeManager dtm; // must be set by test implementation during setUp
@Before
public void setUp() throws Exception {
ToyProgramBuilder builder = new ToyProgramBuilder("notepad", true);
builder.addCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
program = builder.getProgram();
env = new TestEnv();
tool = env.showTool(program);
tool.addPlugin(DataTypeManagerPlugin.class.getName());
plugin = getPlugin(tool, DataTypeManagerPlugin.class);
}
@After
public void tearDown() throws Exception {
env.dispose();
}
@Test
public void testUndoRedo() throws Exception {
Enum enumDt = editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
// delete a row
table.setRowSelectionInterval(0, 0);
runSwing(() -> {
DockingActionIf action = getDeleteAction();
action.actionPerformed(new DefaultActionContext());
});
applyChanges(true);
assertNull(enumDt.getName(0));
// undo
undo(true);
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
//redo
redo(true);
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
}
@Test
public void testUndoRemoval() throws Exception {
editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
assertFalse(model.hasChanges());
undo(true); // will remove enum from DTM
DataType dt = dtm.getDataType("/Category1/Colors");
assertNull(dt);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
assertEquals("Close Enum Editor?", d.getTitle());
JButton button = findButtonByText(d.getComponent(), "Continue Edit");
assertNotNull(button);
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
waitForSwing();
assertTrue(panel.needsSave());
DockingActionIf applyAction = getApplyAction();
assertTrue(applyAction.isEnabled());
applyChanges(true);
dt = dtm.getDataType("/Category1/Colors");
assertNotNull(dt);
}
@Test
public void testChangesBeforeUndoYes() throws Exception {
editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
int origRowCount = model.getRowCount();
runSwing(() -> {
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
applyChanges(true);
// make more changes
runSwing(() -> {
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
undo(false);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
// yes to reload the enum data type
JButton button = findButtonByText(d.getComponent(), "Yes");
assertNotNull(button);
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
waitForSwing();
assertEquals(origRowCount, model.getRowCount());
}
@Test
public void testChangesBeforeUndoNo() throws Exception {
editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
runSwing(() -> {
int lastRow = model.getRowCount() - 1;
if (lastRow >= 0) {
table.addRowSelectionInterval(lastRow, lastRow);
}
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
applyChanges(true);
// make more changes
runSwing(() -> {
int lastRow = model.getRowCount() - 1;
if (lastRow >= 0) {
table.addRowSelectionInterval(lastRow, lastRow);
}
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
int rowCount = model.getRowCount();
undo(false);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
// not to not reload the enum data type
JButton button = findButtonByText(d.getComponent(), "No");
assertNotNull(button);
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
waitForSwing();
assertEquals(rowCount, model.getRowCount());
}
//==================================================================================================
// Private Methods
//==================================================================================================
private EnumEditorPanel findEditorPanel(Window w) {
Window[] windows = w.getOwnedWindows();
for (Window window : windows) {
if (window.isVisible() && JDialog.class.isAssignableFrom(window.getClass())) {
Container c =
findContainer(((JDialog) window).getContentPane(), EnumEditorPanel.class);
if (c != null) {
return (EnumEditorPanel) c;
}
}
}
return null;
}
private Container findContainer(Container parent, Class<?> theClass) {
Component[] c = parent.getComponents();
for (Component element : c) {
if (theClass.isAssignableFrom(element.getClass())) {
return (Container) element;
}
if (element instanceof Container) {
Container container = findContainer((Container) element, theClass);
if (container != null) {
return container;
}
}
}
return null;
}
private void applyChanges(boolean doWait) throws Exception {
DockingActionIf applyAction = getApplyAction();
assertTrue(applyAction.isEnabled());
Runnable r = () -> applyAction.actionPerformed(new DefaultActionContext());
if (doWait) {
runSwing(r);
dtm.flushEvents();
}
else {
runSwingLater(r);
}
waitForSwing();
}
private DockingActionIf getAddAction() {
return getAction(plugin, "Add Enum Value");
}
private DockingActionIf getApplyAction() {
return getAction(plugin, "Apply Enum Changes");
}
private DockingActionIf getDeleteAction() {
return getAction(plugin, "Delete Enum Value");
}
private Enum editSampleEnum() {
AtomicReference<Enum> enumRef = new AtomicReference<>();
dtm.withTransaction("Create Test Enum", () -> {
Category cat = dtm.createCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
enumm.add("Blue", 0x20);
enumm.add("Purple", 5);
enumm.add("Turquoise", 0x22);
enumm.add("Pink", 2);
enumm.setDescription("This is a set of Colors");
Enum enumDt = (Enum) cat.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER);
enumRef.set(enumDt);
dtm.flushEvents();
waitForSwing();
runSwingLater(() -> plugin.edit(enumDt));
});
waitForSwing();
return enumRef.get();
}
private void undo(boolean doWait) throws Exception {
Runnable r = () -> {
try {
undo();
dtm.flushEvents();
}
catch (Exception e) {
Assert.fail(e.getMessage());
}
};
if (doWait) {
runSwing(r);
}
else {
runSwingLater(r);
}
waitForSwing();
}
private void redo(boolean doWait) throws Exception {
Runnable r = () -> {
try {
redo();
dtm.flushEvents();
}
catch (Exception e) {
Assert.fail(e.getMessage());
}
};
if (doWait) {
runSwing(r);
}
else {
runSwingLater(r);
}
waitForSwing();
}
abstract void undo() throws IOException;
abstract void redo() throws IOException;
}
@@ -668,110 +668,6 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
}
@Test
public void testUndoRedo() throws Exception {
Enum enumDt = editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
// delete a row
table.setRowSelectionInterval(0, 0);
runSwing(() -> {
DockingActionIf action = getDeleteAction();
action.actionPerformed(new DefaultActionContext());
});
applyChanges(true);
assertNull(enumDt.getName(0));
// undo
undo(program);
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
//redo
redo(program);
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
}
@Test
public void testChangesBeforeUndoYes() throws Exception {
editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
int origRowCount = model.getRowCount();
runSwing(() -> {
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
applyChanges(true);
// make more changes
runSwing(() -> {
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
undo(false);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
// yes to reload the enum data type
JButton button = findButtonByText(d.getComponent(), "Yes");
assertNotNull(button);
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
waitForSwing();
assertEquals(origRowCount, model.getRowCount());
}
@Test
public void testChangesBeforeUndoNo() throws Exception {
editSampleEnum();
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
EnumTableModel model = (EnumTableModel) table.getModel();
runSwing(() -> {
int lastRow = model.getRowCount() - 1;
if (lastRow >= 0) {
table.addRowSelectionInterval(lastRow, lastRow);
}
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
applyChanges(true);
// make more changes
runSwing(() -> {
int lastRow = model.getRowCount() - 1;
if (lastRow >= 0) {
table.addRowSelectionInterval(lastRow, lastRow);
}
DockingActionIf action = getAddAction();
action.actionPerformed(new DefaultActionContext());
action.actionPerformed(new DefaultActionContext());
});
waitForSwing();
int rowCount = model.getRowCount();
undo(false);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
// not to not reload the enum data type
JButton button = findButtonByText(d.getComponent(), "No");
assertNotNull(button);
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
waitForSwing();
assertEquals(rowCount, model.getRowCount());
}
//==================================================================================================
// Private Methods
//==================================================================================================
@@ -936,23 +832,4 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
return enumDt;
}
private void undo(boolean doWait) throws Exception {
Runnable r = () -> {
try {
program.undo();
program.flushEvents();
}
catch (Exception e) {
Assert.fail(e.getMessage());
}
};
if (doWait) {
runSwing(r);
}
else {
runSwingLater(r);
}
waitForSwing();
}
}
@@ -0,0 +1,71 @@
/* ###
* 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.datamgr.editor;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import org.junit.After;
import org.junit.Before;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.program.model.data.FileDataTypeManager;
public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
private File tempGdt;
private Archive fileArchive;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
tempGdt = createTempFileForTest(".gdt");
tempGdt.delete();
fileArchive = plugin.getDataTypeManagerHandler().createArchive(tempGdt);
assertTrue(fileArchive.isModifiable());
dtm = fileArchive.getDataTypeManager();
}
@After
@Override
public void tearDown() throws Exception {
if (fileArchive != null) {
plugin.getDataTypeManagerHandler().closeArchive(fileArchive);
tempGdt.delete();
}
super.tearDown();
}
@Override
void undo() throws IOException {
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
fileDtm.undo();
}
@Override
void redo() throws IOException {
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
fileDtm.redo();
}
}
@@ -0,0 +1,41 @@
/* ###
* 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.datamgr.editor;
import java.io.IOException;
import org.junit.Before;
public class ProgramEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
@Override
@Before
public void setUp() throws Exception {
super.setUp();
dtm = program.getDataTypeManager();
}
@Override
void undo() throws IOException {
program.undo();
}
@Override
void redo() throws IOException {
program.redo();
}
}
@@ -0,0 +1,69 @@
/* ###
* 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.datamgr.editor;
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.After;
import org.junit.Before;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.framework.model.DomainFolder;
import ghidra.program.database.DataTypeArchiveDB;
public class ProjectArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
Archive projectArchive;
DataTypeArchiveDB dataTypeArchiveDB;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
dataTypeArchiveDB = new DataTypeArchiveDB(rootFolder, "Test", tool);
projectArchive = plugin.getDataTypeManagerHandler().openArchive(dataTypeArchiveDB);
assertTrue(projectArchive.isModifiable());
dtm = dataTypeArchiveDB.getDataTypeManager();
}
@After
@Override
public void tearDown() throws Exception {
if (projectArchive != null) {
plugin.getDataTypeManagerHandler().closeArchive(projectArchive);
}
super.tearDown();
}
@Override
void undo() throws IOException {
dataTypeArchiveDB.undo();
}
@Override
void redo() throws IOException {
dataTypeArchiveDB.redo();
}
}
@@ -22,8 +22,6 @@ import javax.swing.JTextField;
import org.junit.Before;
import org.junit.Test;
import ghidra.framework.model.*;
import ghidra.program.model.data.DataTypeManagerDomainObject;
import ghidra.program.model.data.Pointer;
public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
@@ -113,39 +111,4 @@ public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
assertEquals(0x4, stackModel.getParameterSize());
}
// public void testIncreasePosReturnAddrOffset() throws Exception {
// init(SIMPLE_STACK);
// assertEquals(0x20, stackModel.getFrameSize());
// assertEquals(0x0, stackModel.getReturnAddressOffset());
// assertEquals(0x12, stackModel.getLocalSize());
// assertEquals(-0x8, stackModel.getParameterOffset());
// assertEquals(0x7, stackModel.getParameterSize());
// }
//
// public void testDecreasePosReturnAddrOffset() throws Exception {
// init(SIMPLE_STACK);
// assertEquals(0x20, stackModel.getFrameSize());
// assertEquals(0x0, stackModel.getReturnAddressOffset());
// assertEquals(0x12, stackModel.getLocalSize());
// assertEquals(-0x8, stackModel.getParameterOffset());
// assertEquals(0x7, stackModel.getParameterSize());
// }
protected class RestoreListener implements DomainObjectListener {
/**
* @see ghidra.framework.model.DomainObjectListener#domainObjectChanged(ghidra.framework.model.DomainObjectChangedEvent)
*/
@Override
public void domainObjectChanged(DomainObjectChangedEvent event) {
if (event.contains(DomainObjectEvent.RESTORED)) {
Object source = event.getSource();
if (source instanceof DataTypeManagerDomainObject) {
DataTypeManagerDomainObject restoredDomainObject =
(DataTypeManagerDomainObject) source;
provider.domainObjectRestored(restoredDomainObject);
}
}
}
}
}
@@ -1119,6 +1119,11 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
// don't care for now
}
@Override
public void restored(DataTypeManager dataTypeManager) {
// don't care for now
}
}
private class CustomDataType extends StructureDataType {
@@ -910,5 +910,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
// don't care
}
@Override
public void restored(DataTypeManager dataTypeManager) {
// don't care
}
}
}
@@ -476,13 +476,21 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter implemen
@Override
public void invalidate() {
clearCache(false);
super.invalidate();
super.invalidate(); // fires RESTORED event
}
protected void clearCache(boolean all) {
options.clearCache();
}
/**
* Indicates that this domain object has been restored to a completely different state due
* to a transaction undo/redo/rollback or a database merge operation.
*/
protected void domainObjectRestored() {
invalidate();
}
@Override
public synchronized boolean canSave() {
DomainFile df = getDomainFile();

Some files were not shown because too many files have changed in this diff Show More