mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-22 18:14:09 +08:00
GP-6779 Refactoring DataTypeManagers/Archives
This commit is contained in:
@@ -32,8 +32,9 @@ import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.ProjectData;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -132,7 +133,9 @@ public class IsfServer extends Thread {
|
||||
|
||||
private DataTypeManager openAsDataTypeArchive(String ns) throws Exception {
|
||||
File gdt = new File(ns);
|
||||
return FileDataTypeManager.openFileArchive(gdt, false);
|
||||
FileDataTypeArchive archive =
|
||||
DataTypeArchiveFactory.openReadOnly(gdt, this, TaskMonitor.DUMMY);
|
||||
return archive != null ? archive.getDataTypeManager() : null;
|
||||
}
|
||||
|
||||
private DataTypeManager openAsProgramDatabase(String ns) throws Exception {
|
||||
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -16,14 +16,14 @@
|
||||
package ghidra.trace.database;
|
||||
|
||||
import db.util.ErrorHandler;
|
||||
import ghidra.program.database.ManagerDB;
|
||||
import ghidra.program.database.ProgramDBModule;
|
||||
|
||||
public interface DBTraceManager extends ErrorHandler {
|
||||
/**
|
||||
* Invalidate this manager's caches
|
||||
*
|
||||
* @param all probably nothing. Check out implementations of
|
||||
* {@link ManagerDB#invalidateCache(boolean)}.
|
||||
* {@link ProgramDBModule#invalidateCache(boolean)}.
|
||||
*/
|
||||
void invalidateCache(boolean all);
|
||||
}
|
||||
|
||||
+9
-37
@@ -18,7 +18,6 @@ package ghidra.trace.database.data;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.Transaction;
|
||||
@@ -27,13 +26,14 @@ import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.database.data.ProgramBasedDataTypeManagerDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.dtarchive.DataTypeStore;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.DBTracePlatformManager.DBTraceHostPlatform;
|
||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
@@ -42,17 +42,6 @@ import ghidra.util.task.TaskMonitor;
|
||||
public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
implements TraceBasedDataTypeManager, DBTraceManager {
|
||||
|
||||
/**
|
||||
* NOTE: This "read-write" lock is actually just a compatibility wrapper around the
|
||||
* {@link ghidra.util.Lock} for the entire trace database. There was a time when I dreamed of
|
||||
* using an actual read-write lock (though it's not known if that'd actually achieve any
|
||||
* appreciable speed up); however, inheriting the existing DataTypeManager implementation
|
||||
* required its lock to be used throughout the database. Rather than convert all my code (and
|
||||
* lose the distinction of where I need write vs. read locks), I just wrapped the API. So no,
|
||||
* this code does not refer to the wrapper, but it does still use the lock. I keep a reference
|
||||
* to it here in case I ever need it.
|
||||
*/
|
||||
protected final ReadWriteLock lock;
|
||||
protected final DBTrace trace;
|
||||
protected final InternalTracePlatform platform;
|
||||
|
||||
@@ -64,11 +53,10 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
};
|
||||
}
|
||||
|
||||
public DBTraceDataTypeManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock,
|
||||
public DBTraceDataTypeManager(DBHandle dbh, OpenMode openMode, Lock lock,
|
||||
TaskMonitor monitor, DBTrace trace, InternalTracePlatform platform)
|
||||
throws CancelledException, VersionException, IOException {
|
||||
super(dbh, null, openMode, computePrefix(platform), trace, trace.getLock(), monitor);
|
||||
this.lock = lock; // TODO: nothing uses this local lock - not sure what its purpose is
|
||||
super(dbh, null, openMode, computePrefix(platform), trace, lock, monitor);
|
||||
this.trace = trace;
|
||||
this.platform = platform;
|
||||
|
||||
@@ -79,6 +67,11 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTypeStore getDataStore() {
|
||||
return trace.getProgramView();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dataSettingChanged(Address address) {
|
||||
// ignored - instance settings are not current supported (no AddressMap provided)
|
||||
@@ -99,16 +92,6 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
return trace.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) throws InvalidNameException {
|
||||
if (name == null || name.length() == 0) {
|
||||
throw new InvalidNameException("Name must be at least one character long: " + name);
|
||||
}
|
||||
|
||||
trace.setName(name);
|
||||
categoryRenamed(CategoryPath.ROOT, getCategory(CategoryPath.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTracePlatform getPlatform() {
|
||||
return platform;
|
||||
@@ -239,23 +222,12 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
return trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainFile getDomainFile() {
|
||||
return trace.getDomainFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDomainFileID() {
|
||||
DomainFile domainFile = trace.getDomainFile(); // Can be null if never saved
|
||||
return domainFile == null ? null : domainFile.getFileID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
DomainFile domainFile = trace.getDomainFile(); // Can be null if never saved
|
||||
return domainFile == null ? null : domainFile.getPathname();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getType() {
|
||||
/**
|
||||
|
||||
+3
-3
@@ -17,7 +17,6 @@ package ghidra.trace.database.guest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
@@ -33,6 +32,7 @@ import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.trace.model.guest.*;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStore;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory;
|
||||
@@ -49,7 +49,7 @@ import ghidra.util.task.TaskMonitor;
|
||||
*/
|
||||
public class DBTracePlatformManager implements DBTraceManager, TracePlatformManager {
|
||||
protected final DBHandle dbh;
|
||||
protected final ReadWriteLock lock;
|
||||
protected final Lock lock;
|
||||
protected final Language baseLanguage;
|
||||
protected final CompilerSpec baseCompilerSpec;
|
||||
protected final DBTrace trace;
|
||||
@@ -157,7 +157,7 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
||||
|
||||
protected final InternalTracePlatform hostPlatform = new DBTraceHostPlatform();
|
||||
|
||||
public DBTracePlatformManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock,
|
||||
public DBTracePlatformManager(DBHandle dbh, OpenMode openMode, Lock lock,
|
||||
TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
this.dbh = dbh;
|
||||
|
||||
+7
-4
@@ -56,9 +56,8 @@ import ghidra.trace.model.bookmark.TraceBookmark;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkType;
|
||||
import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.memory.TraceMemoryOperations.StatePredicate;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
@@ -599,8 +598,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
protected static class OverlappingAddressRangeKeyIteratorMerger<T> extends
|
||||
PairingIteratorMerger<Entry<AddressRange, T>, Entry<AddressRange, T>,
|
||||
Entry<AddressRange, T>> {
|
||||
PairingIteratorMerger<Entry<AddressRange, T>, Entry<AddressRange, T>, Entry<AddressRange, T>> {
|
||||
|
||||
protected static <T> Iterable<Pair<Entry<AddressRange, T>, Entry<AddressRange, T>>> iter(
|
||||
Iterable<Entry<AddressRange, T>> left, Iterable<Entry<AddressRange, T>> right) {
|
||||
@@ -1565,4 +1563,9 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
return queues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getArchiveType() {
|
||||
return ArchiveType.PROGRAM;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -169,12 +169,12 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(ProgramDB program) {
|
||||
public void setDomainObject(ProgramDB program) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor)
|
||||
public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import java.util.Collection;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.DataTypeManagerDomainObject;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@@ -60,7 +60,7 @@ import ghidra.util.LockHold;
|
||||
* specific snapshot and/or manipulate the trace as if it were a program, use
|
||||
* {@link #getProgramView()}.
|
||||
*/
|
||||
public interface Trace extends DataTypeManagerDomainObject {
|
||||
public interface Trace extends DomainObject {
|
||||
Icon TRACE_ICON = new GIcon("icon.content.handler.trace");
|
||||
|
||||
public interface TraceProgramViewListener {
|
||||
@@ -92,7 +92,6 @@ public interface Trace extends DataTypeManagerDomainObject {
|
||||
* For traces, this gets the "base" or "host" {@link DataTypeManager}. For platform-specific
|
||||
* managers, see {@link TracePlatform#getDataTypeManager()}.
|
||||
*/
|
||||
@Override
|
||||
default TraceBasedDataTypeManager getDataTypeManager() {
|
||||
return getBaseDataTypeManager();
|
||||
}
|
||||
|
||||
+8
-11
@@ -25,7 +25,9 @@ import java.nio.file.Path;
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
@@ -77,22 +79,17 @@ public class DBTraceDataTypeManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
assertEquals("Testing", dtm.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetName() throws InvalidNameException {
|
||||
try (Transaction tx = trace.openTransaction("Testing")) {
|
||||
dtm.setName("Another name");
|
||||
}
|
||||
assertEquals("Another name", trace.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSourceArchive() throws IOException {
|
||||
StructureDataType mine = getTestDataType();
|
||||
DataTypePath minePath = mine.getDataTypePath();
|
||||
Path tmpDir = Files.createTempDirectory("test");
|
||||
File archiveFile = tmpDir.resolve("test.gdt").toFile();
|
||||
FileDataTypeManager dtm2 = FileDataTypeManager.createFileArchive(archiveFile);
|
||||
try (Transaction tx = dtm2.openTransaction("Testing")) {
|
||||
|
||||
FileDataTypeArchive archive = DataTypeArchiveFactory.createFileArchive(archiveFile, this);
|
||||
DataTypeManager dtm2 = archive.getDataTypeManager();
|
||||
|
||||
try (Transaction tx = archive.openTransaction("Testing")) {
|
||||
dtm2.addDataType(mine, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
DataType got = dtm2.getDataType(minePath);
|
||||
@@ -100,7 +97,7 @@ public class DBTraceDataTypeManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
try (Transaction tx = trace.openTransaction("Testing")) {
|
||||
dtm.addDataType(got, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
dtm2.delete();
|
||||
archive.delete();
|
||||
|
||||
// TODO: Listen for sourceArchiveAdded event
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@ import java.util.*;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning;
|
||||
import ghidra.program.model.dtarchive.ArchiveWarning;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
public class CompareGDTs extends GhidraScript {
|
||||
@@ -35,8 +36,10 @@ public class CompareGDTs extends GhidraScript {
|
||||
private File firstFile;
|
||||
private File secondFile;
|
||||
private File outputFile;
|
||||
private FileDataTypeManager firstArchive;
|
||||
private FileDataTypeManager secondArchive;
|
||||
private FileDataTypeArchive archive1;
|
||||
private FileDataTypeArchive archive2;
|
||||
private DataTypeManager dtm1;
|
||||
private DataTypeManager dtm2;
|
||||
private PrintWriter printWriter;
|
||||
boolean matchByName;
|
||||
boolean checkPointers;
|
||||
@@ -57,23 +60,24 @@ public class CompareGDTs extends GhidraScript {
|
||||
}
|
||||
}
|
||||
|
||||
firstArchive = openDataTypeArchive(firstFile, false);
|
||||
if (firstArchive.getWarning() != ArchiveWarning.NONE) {
|
||||
archive1 = openFileDataTypeArchive(firstFile, false);
|
||||
if (archive1.getWarning() != ArchiveWarning.NONE) {
|
||||
popup(
|
||||
"An architecture language error occured while opening archive (see log for details)\n" +
|
||||
firstFile.getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
secondArchive = openDataTypeArchive(secondFile, false);
|
||||
if (secondArchive.getWarning() != ArchiveWarning.NONE) {
|
||||
archive2 = openFileDataTypeArchive(secondFile, false);
|
||||
if (archive2.getWarning() != ArchiveWarning.NONE) {
|
||||
popup(
|
||||
"An architecture language error occured while opening archive (see log for details)\n" +
|
||||
secondFile.getPath());
|
||||
firstArchive.close();
|
||||
archive1.release(this);
|
||||
return;
|
||||
}
|
||||
|
||||
dtm1 = archive1.getDataTypeManager();
|
||||
dtm2 = archive2.getDataTypeManager();
|
||||
matchByName = askYesNo("Match Data Types By Path Name?",
|
||||
"Do you want to match data types by their path names (rather than by Universal ID)?");
|
||||
checkPointers = askYesNo("Check Pointers?", "Do you want to check Pointers?");
|
||||
@@ -85,8 +89,8 @@ public class CompareGDTs extends GhidraScript {
|
||||
}
|
||||
finally {
|
||||
printWriter.close();
|
||||
firstArchive.close();
|
||||
secondArchive.close();
|
||||
archive1.release(this);
|
||||
archive2.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,30 +104,30 @@ public class CompareGDTs extends GhidraScript {
|
||||
secondFile.getAbsolutePath() + ".");
|
||||
|
||||
output("\nThe following data types are only in " + firstFile.getAbsolutePath() + ".");
|
||||
long onlyInFirst = outputEachDataTypeOnlyInFirst(firstArchive, secondArchive);
|
||||
long onlyInFirst = outputEachDataTypeOnlyInFirst(dtm1, dtm2);
|
||||
output(onlyInFirst + " data types that were only in first archive.");
|
||||
|
||||
output("\nThe following data types are only in " + secondFile.getAbsolutePath() + ".");
|
||||
long onlyInSecond = outputEachDataTypeOnlyInFirst(secondArchive, firstArchive);
|
||||
long onlyInSecond = outputEachDataTypeOnlyInFirst(dtm2, dtm1);
|
||||
output(onlyInSecond + " data types that were only in second archive.");
|
||||
|
||||
output("\nThe following are different kinds of data types.");
|
||||
long differentKinds = outputWhereTypesDiffer(firstArchive, secondArchive);
|
||||
long differentKinds = outputWhereTypesDiffer(dtm1, dtm2);
|
||||
output(differentKinds + " data types had different types.");
|
||||
|
||||
output("\nThe following data types are defined differently.");
|
||||
long differentDefinitions = outputWhereDefinitionsDiffer(firstArchive, secondArchive);
|
||||
long differentDefinitions = outputWhereDefinitionsDiffer(dtm1, dtm2);
|
||||
output(differentDefinitions + " data types had different definitions.");
|
||||
|
||||
output("\nThe following data types are different sizes");
|
||||
long differentSizes = outputWhereSizesDiffer(firstArchive, secondArchive);
|
||||
long differentSizes = outputWhereSizesDiffer(dtm1, dtm2);
|
||||
output(differentSizes + " data types had different sizes.");
|
||||
|
||||
output("\n");
|
||||
}
|
||||
|
||||
private long outputEachDataTypeOnlyInFirst(FileDataTypeManager dtmArchive1,
|
||||
FileDataTypeManager dtmArchive2) {
|
||||
private long outputEachDataTypeOnlyInFirst(DataTypeManager dtmArchive1,
|
||||
DataTypeManager dtmArchive2) {
|
||||
|
||||
long missingCount = 0;
|
||||
Iterator<DataType> allDataTypes = dtmArchive1.getAllDataTypes();
|
||||
@@ -136,7 +140,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
return missingCount;
|
||||
}
|
||||
|
||||
private boolean outputIfMissingDataType(DataType dataType, FileDataTypeManager dtmArchive) {
|
||||
private boolean outputIfMissingDataType(DataType dataType, DataTypeManager dtmArchive) {
|
||||
|
||||
if (!checkPointers && dataType instanceof Pointer) {
|
||||
return false;
|
||||
@@ -155,7 +159,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
return false;
|
||||
}
|
||||
|
||||
private DataType getMatchingDataType(DataType dataType, FileDataTypeManager dtmArchive) {
|
||||
private DataType getMatchingDataType(DataType dataType, DataTypeManager dtmArchive) {
|
||||
|
||||
if (!matchByName) {
|
||||
UniversalID universalID = dataType.getUniversalID();
|
||||
@@ -172,17 +176,20 @@ public class CompareGDTs extends GhidraScript {
|
||||
|
||||
// find by name
|
||||
List<DataType> list = new ArrayList<DataType>();
|
||||
dtmArchive.findDataTypes(dataType.getName(), list );
|
||||
dtmArchive.findDataTypes(dataType.getName(), list);
|
||||
for (DataType dtc : list) {
|
||||
if (dataType.getCategoryPath().getPath().toLowerCase().equals(dtc.getCategoryPath().getPath().toLowerCase())) {
|
||||
if (dataType.getCategoryPath()
|
||||
.getPath()
|
||||
.toLowerCase()
|
||||
.equals(dtc.getCategoryPath().getPath().toLowerCase())) {
|
||||
return dtc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private long outputWhereTypesDiffer(FileDataTypeManager dtmArchive1,
|
||||
FileDataTypeManager dtmArchive2) {
|
||||
private long outputWhereTypesDiffer(DataTypeManager dtmArchive1,
|
||||
DataTypeManager dtmArchive2) {
|
||||
|
||||
long differCount = 0;
|
||||
Iterator<DataType> allDataTypes = dtmArchive1.getAllDataTypes();
|
||||
@@ -195,8 +202,8 @@ public class CompareGDTs extends GhidraScript {
|
||||
return differCount;
|
||||
}
|
||||
|
||||
private long outputWhereSizesDiffer(FileDataTypeManager dtmArchive1,
|
||||
FileDataTypeManager dtmArchive2) {
|
||||
private long outputWhereSizesDiffer(DataTypeManager dtmArchive1,
|
||||
DataTypeManager dtmArchive2) {
|
||||
|
||||
long differCount = 0;
|
||||
Iterator<DataType> allDataTypes = dtmArchive1.getAllDataTypes();
|
||||
@@ -209,7 +216,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
return differCount;
|
||||
}
|
||||
|
||||
private boolean outputIfDifferentTypes(DataType dataType, FileDataTypeManager dtmArchive) {
|
||||
private boolean outputIfDifferentTypes(DataType dataType, DataTypeManager dtmArchive) {
|
||||
|
||||
if (!checkPointers && dataType instanceof Pointer) {
|
||||
return false;
|
||||
@@ -233,8 +240,8 @@ public class CompareGDTs extends GhidraScript {
|
||||
return false;
|
||||
}
|
||||
|
||||
private long outputWhereDefinitionsDiffer(FileDataTypeManager dtmArchive1,
|
||||
FileDataTypeManager dtmArchive2) {
|
||||
private long outputWhereDefinitionsDiffer(DataTypeManager dtmArchive1,
|
||||
DataTypeManager dtmArchive2) {
|
||||
|
||||
long differCount = 0;
|
||||
Iterator<DataType> allDataTypes = dtmArchive1.getAllDataTypes();
|
||||
@@ -248,7 +255,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
}
|
||||
|
||||
private boolean outputIfDifferentDefinitions(DataType dataType,
|
||||
FileDataTypeManager dtmArchive) {
|
||||
DataTypeManager dtmArchive) {
|
||||
|
||||
if (!checkPointers && dataType instanceof Pointer) {
|
||||
return false;
|
||||
@@ -263,7 +270,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
Class<?> dtClass = dataType.getClass();
|
||||
Class<?> sameNamedDtClass = matchingDataType.getClass();
|
||||
if (dtClass == sameNamedDtClass) {
|
||||
if (dataType instanceof Enum && (((Enum) dataType).getCount()==1)) {
|
||||
if (dataType instanceof Enum && (((Enum) dataType).getCount() == 1)) {
|
||||
// don't check single entry enums. Size will vary, and they are extracted defines
|
||||
return checkEnum((Enum) dataType, (Enum) matchingDataType);
|
||||
}
|
||||
@@ -283,18 +290,18 @@ public class CompareGDTs extends GhidraScript {
|
||||
return true;
|
||||
}
|
||||
// Check that the name is the same
|
||||
if (! e1.getNames()[0].equals(e2.getNames()[0])) {
|
||||
if (!e1.getNames()[0].equals(e2.getNames()[0])) {
|
||||
return true;
|
||||
}
|
||||
// Check the value is the same
|
||||
if (e1.getValues()[0] != e2.getValues()[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean outputIfDifferentSizes(DataType dataType, FileDataTypeManager dtmArchive) {
|
||||
private boolean outputIfDifferentSizes(DataType dataType, DataTypeManager dtmArchive) {
|
||||
|
||||
if (!checkPointers && dataType instanceof Pointer) {
|
||||
return false;
|
||||
@@ -309,7 +316,7 @@ public class CompareGDTs extends GhidraScript {
|
||||
Class<?> dtClass = dataType.getClass();
|
||||
Class<?> sameNamedDtClass = matchingDataType.getClass();
|
||||
if (dtClass == sameNamedDtClass) {
|
||||
if (dataType instanceof Enum && (((Enum) dataType).getCount()==1)) {
|
||||
if (dataType instanceof Enum && (((Enum) dataType).getCount() == 1)) {
|
||||
// don't check single entry enums. Size will vary, and they are extracted defines
|
||||
return checkEnum((Enum) dataType, (Enum) matchingDataType);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -35,12 +35,14 @@ import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.util.cparser.C.CParserUtils;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class CreateUEFIGDTArchivesScript extends GhidraScript {
|
||||
|
||||
private File outputDirectory;
|
||||
|
||||
|
||||
private static String headerFilePath = "/data/HeaderFiles/git/edk2";
|
||||
|
||||
@Override
|
||||
@@ -49,41 +51,49 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript {
|
||||
|
||||
parseUEFIHeaders("X64", "x86:LE:64:default", "windows");
|
||||
parseUEFIHeaders("Ia32", "x86:LE:32:default", "windows");
|
||||
|
||||
|
||||
parseUEFIHeaders("AArch64", "AARCH64:LE:64:v8A", "windows");
|
||||
parseUEFIHeaders("Arm", "ARM:LE:32:v8", "default");
|
||||
|
||||
|
||||
parseUEFIHeaders("RiscV64", "RISCV:LE:64:RV64G", "gcc");
|
||||
parseUEFIHeaders("LoongArch64", "Loongarch:LE:64:lp64d", "default");
|
||||
}
|
||||
|
||||
private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler,
|
||||
String[] filenames, String includePaths[], String[] args)
|
||||
|
||||
private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID,
|
||||
String compiler, String[] filenames, String includePaths[], String[] args)
|
||||
throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException {
|
||||
DataTypeManager openTypes[] = null;
|
||||
|
||||
parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args);
|
||||
|
||||
parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames,
|
||||
includePaths, args);
|
||||
}
|
||||
|
||||
private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler,
|
||||
String[] filenames, String[] includePaths, String[] args)
|
||||
private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName,
|
||||
String languageID, String compiler, String[] filenames, String[] includePaths,
|
||||
String[] args)
|
||||
throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException {
|
||||
|
||||
String dataTypeFile = outputDir + File.separator + gdtName + ".gdt";
|
||||
|
||||
File f = getArchiveFile(dataTypeFile);
|
||||
|
||||
FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames,
|
||||
includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor);
|
||||
|
||||
dtMgr.save();
|
||||
dtMgr.close();
|
||||
String dataTypeFile = outputDir + File.separator + gdtName + ".gdt";
|
||||
|
||||
File f = getArchiveFile(dataTypeFile);
|
||||
|
||||
FileDataTypeArchive archive = CParserUtils.parseHeaderFiles(filenames,
|
||||
includePaths, args, f.getAbsolutePath(), languageID, compiler, openTypes, this,
|
||||
monitor);
|
||||
|
||||
try {
|
||||
archive.save(null, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen since a DUMMY monitor was used
|
||||
}
|
||||
archive.release(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn string into a file, delete old archive if it exists
|
||||
*
|
||||
* @param dataTypeFile
|
||||
* @param dataTypeFile the file name of the stored archive
|
||||
*
|
||||
* @return file
|
||||
*/
|
||||
@@ -99,9 +109,9 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript {
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
public void parseUEFIHeaders(String name, String languageID, String compiler) throws Exception {
|
||||
|
||||
|
||||
String filenames[] = {
|
||||
"ProcessorBind.h",
|
||||
"Uefi/UefiBaseType.h",
|
||||
@@ -116,22 +126,23 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript {
|
||||
"Library/StandaloneMmDriverEntryPoint.h",
|
||||
"Library/UefiApplicationEntryPoint.h",
|
||||
"Library/UefiDriverEntryPoint.h",
|
||||
headerFilePath+"/MdePkg/Include/Pi/",
|
||||
headerFilePath+"/MdePkg/Include/Ppi/",
|
||||
headerFilePath+"/MdePkg/Include/Protocol/",
|
||||
headerFilePath+"/MdePkg/Include/IndustryStandard/",
|
||||
headerFilePath + "/MdePkg/Include/Pi/",
|
||||
headerFilePath + "/MdePkg/Include/Ppi/",
|
||||
headerFilePath + "/MdePkg/Include/Protocol/",
|
||||
headerFilePath + "/MdePkg/Include/IndustryStandard/",
|
||||
};
|
||||
|
||||
|
||||
String includePaths[] = {
|
||||
headerFilePath+"/MdePkg/Include/"+name,
|
||||
headerFilePath+"/MdePkg/Include",
|
||||
headerFilePath + "/MdePkg/Include/" + name,
|
||||
headerFilePath + "/MdePkg/Include",
|
||||
};
|
||||
|
||||
|
||||
String args[] = {
|
||||
"-DSTATIC_ASSERT=static_assert"
|
||||
};
|
||||
|
||||
parseHeaderFilesToGDT(outputDirectory, "uefi_"+name, languageID, compiler, filenames, includePaths, args);
|
||||
|
||||
parseHeaderFilesToGDT(outputDirectory, "uefi_" + name, languageID, compiler, filenames,
|
||||
includePaths, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -29,10 +29,11 @@ import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProjectDataTypeManager;
|
||||
import ghidra.program.database.data.*;
|
||||
import ghidra.program.database.data.DataTypeManagerDB;
|
||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||
import ghidra.program.model.data.BuiltInDataTypeManager;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class FixupCompositeDataTypesScript extends GhidraScript {
|
||||
@@ -50,33 +51,30 @@ public class FixupCompositeDataTypesScript extends GhidraScript {
|
||||
popup("This script requires the DataTypeManagerService");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ArrayList<DTMWrapper> dtms = new ArrayList<>();
|
||||
|
||||
for (DataTypeManager dtm : service.getDataTypeManagers()) {
|
||||
if (dtm instanceof BuiltInDataTypeManager) {
|
||||
continue;
|
||||
}
|
||||
if (dtm instanceof ProgramDataTypeManager) {
|
||||
dtms.add(0, new DTMWrapper((ProgramDataTypeManager) dtm));
|
||||
}
|
||||
else if (dtm instanceof DataTypeManagerDB) {
|
||||
dtms.add(new DTMWrapper((DataTypeManagerDB) dtm));
|
||||
if (dtm instanceof DataTypeManagerDB dtmDB) {
|
||||
dtms.add(new DTMWrapper(dtmDB));
|
||||
}
|
||||
}
|
||||
|
||||
DataTypeManagerDB dtm =
|
||||
askChoice("Fixup All Composites", "Select Data Type Manager: ", dtms, dtms.get(0)).dtm;
|
||||
|
||||
if (dtm instanceof ProgramDataTypeManager) {
|
||||
Program program = ((ProgramDataTypeManager) dtm).getProgram();
|
||||
if (dtm instanceof ProgramDataTypeManager programDtm) {
|
||||
Program program = programDtm.getProgram();
|
||||
if (!program.hasExclusiveAccess()) {
|
||||
popup("Shared program must have an exclusive checkout.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (dtm instanceof ProjectDataTypeManager) {
|
||||
DomainFile df = ((ProjectDataTypeManager) dtm).getDomainFile();
|
||||
else if (dtm.getDataStore() instanceof ProjectDataTypeArchive projectArchive) {
|
||||
DomainFile df = projectArchive.getDomainFile();
|
||||
if (df.isVersioned() && !df.isCheckedOutExclusive()) {
|
||||
popup("Shared project archive must have an exclusive checkout.");
|
||||
return;
|
||||
@@ -87,7 +85,6 @@ public class FixupCompositeDataTypesScript extends GhidraScript {
|
||||
popup("Selected archive must be open for update.");
|
||||
return;
|
||||
}
|
||||
|
||||
dtm.fixupComposites(monitor);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -22,48 +22,49 @@
|
||||
import java.io.File;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.data.Category;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning;
|
||||
import ghidra.program.model.dtarchive.ArchiveWarning;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SynchronizeGDTCategoryPaths extends GhidraScript {
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
|
||||
File firstFile = askFile("Select First GDT File", "Select 1st");
|
||||
try (FileDataTypeManager firstArchive =
|
||||
FileDataTypeManager.openFileArchive(firstFile, false)) {
|
||||
if (hasWarning(firstArchive, firstFile)) {
|
||||
FileDataTypeArchive archive1 = null;
|
||||
FileDataTypeArchive archive2 = null;
|
||||
try {
|
||||
File file1 = askFile("Select First GDT File", "Select 1st");
|
||||
archive1 = DataTypeArchiveFactory.openReadOnly(file1, this, TaskMonitor.DUMMY);
|
||||
if (hasWarning(archive1, file1)) {
|
||||
return;
|
||||
}
|
||||
File file2 = askFile("Select Second GDT File", "Select 2nd");
|
||||
archive2 = DataTypeArchiveFactory.openReadOnly(file2, this, TaskMonitor.DUMMY);
|
||||
if (hasWarning(archive2, file2)) {
|
||||
return;
|
||||
}
|
||||
Category firstCategory = archive1.getDataTypeManager().getRootCategory();
|
||||
Category secondCategory = archive2.getDataTypeManager().getRootCategory();
|
||||
|
||||
File secondFile = askFile("Select Second GDT File", "Select 2nd");
|
||||
try (FileDataTypeManager secondArchive =
|
||||
FileDataTypeManager.openFileArchive(secondFile, true)) {
|
||||
if (hasWarning(secondArchive, secondFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int transactionID =
|
||||
secondArchive.startTransaction("Synchronize Category Path Names");
|
||||
try {
|
||||
Category firstCategory = firstArchive.getRootCategory();
|
||||
Category secondCategory = secondArchive.getRootCategory();
|
||||
synchronizeCategory(firstCategory, secondCategory);
|
||||
}
|
||||
finally {
|
||||
secondArchive.endTransaction(transactionID, true);
|
||||
secondArchive.save();
|
||||
secondArchive.close();
|
||||
}
|
||||
archive2.withTransaction("Synchronized Category Path Names",
|
||||
() -> synchronizeCategory(firstCategory, secondCategory));
|
||||
}
|
||||
finally {
|
||||
if (archive1 != null) {
|
||||
archive1.release(this);
|
||||
}
|
||||
if (archive2 != null) {
|
||||
archive2.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasWarning(FileDataTypeManager archive, File file) {
|
||||
private boolean hasWarning(FileDataTypeArchive archive, File file) {
|
||||
ArchiveWarning warning = archive.getWarning();
|
||||
if (warning == ArchiveWarning.NONE) {
|
||||
return false;
|
||||
|
||||
+9
-22
@@ -94,7 +94,7 @@
|
||||
<I>Data Type Manager</I> window. When a data type archive is open, it
|
||||
is displayed as a node in the <I>Data Type Manager</I>
|
||||
tree. Archives can be opened by the user or automatically when a program is opened which
|
||||
references that archive. Data type archives can be <A href="#open_for_editing">open for
|
||||
references that archive. Data type archives can be <A href="#Open_Archive_For_Editing">open for
|
||||
modification</A> or as read-only. Within the <I>Data Type Manager</I> window there are
|
||||
actions for opening, closing, renaming, and making archives modifiable.</P>
|
||||
</BLOCKQUOTE>
|
||||
@@ -243,17 +243,6 @@
|
||||
the program</B>.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Update_Source_Archive_Names">Update Source Archive Names</A></H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>When new releases of Ghidra are installed, old datatype archives may be dropped. In some
|
||||
cases these old archives may be replaced with a differently named archive. When this
|
||||
happens, this popup action can be used on a client archive to update the old source archive
|
||||
references. This popup action is only available when such an archive name change has
|
||||
occurred. Any programs or other client archives which reference the old archive will have
|
||||
this popup action available to update the source archive name reference.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Committing Changes To Source Archive</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
@@ -349,7 +338,7 @@
|
||||
files are organized into directories in a filesystem. Archives are useful for sharing data
|
||||
types with other users, or making your data types available for use in other projects.
|
||||
Normally, file archives are opened in a read-only mode, but can optionally be <A href=
|
||||
"#open_for_editing">opened</A> for editing. Project archives are normally opened for
|
||||
"#Open_Archive_For_Editing">opened</A> for editing. Project archives are normally opened for
|
||||
editing, since they support sharing and version control and therefore allow more than one
|
||||
user to modify them at a time. Only one user at a time can have a file archive opened for
|
||||
editing.</P>
|
||||
@@ -467,7 +456,7 @@
|
||||
Archive</B></I> action. The archive will be removed from the tree.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="open_for_editing"></A>Opening a File Data Type Archive for Editing</H3>
|
||||
<H3><A name="Open_Archive_For_Editing"></A>Opening a File Data Type Archive for Editing</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>When an archive is first opened, it is not editable. In order to make any changes to
|
||||
@@ -479,13 +468,13 @@
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<DIV align="left">
|
||||
<H3><A name="Unlock_Archive"></A> <A name="Lock_Archive"></A>Closing a File Data Type
|
||||
<H3><A name></A> <A name="Close_Archive_For_Editing"></A>Closing a File Data Type
|
||||
Archive for Editing<BR>
|
||||
</H3>
|
||||
</DIV>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>When an archive that is <A href="#open_for_editing">open for editing</A> no longer
|
||||
<P>When an archive that is <A href="#Open_Archive_For_Editing">open for editing</A> no longer
|
||||
needs to be edited, then it should be put back to a read-only mode so that other users
|
||||
can then modify it. Select the data type archive to close for editing, right-click on it
|
||||
and select the <I><B>Close for Editing</B></I> action. If the archive has unsaved
|
||||
@@ -502,7 +491,6 @@
|
||||
changes will be saved and the name will be updated to not show a '*'.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<!-- Unimplemented Action
|
||||
<H3><A name="Save_As"></A>Saving a File Data Type Archive to a New File<BR>
|
||||
</H3>
|
||||
|
||||
@@ -512,7 +500,6 @@
|
||||
and filename for the new archive that will be created. The tree will be updated to show
|
||||
the new name for the archive (the filename). The original archive file is unaffected.</P>
|
||||
</BLOCKQUOTE>
|
||||
-->
|
||||
|
||||
<H3><A name="Undo_Archive_Change"></A>Undo Unsaved Archive Change</H3>
|
||||
|
||||
@@ -523,7 +510,7 @@
|
||||
stack of unsaved changes. The next change which may be reverted is described by the archive's
|
||||
Undo Change popup menu item. If this action is used and a change is reverted it may be re-applied by using the
|
||||
<A href="#Redo_Archive_Change">Redo Change</A> action. When the data type archive is
|
||||
<A href="#Save">saved</A> or <A href="#Lock_Archive">closed for editing</A> the undo/redo stack is
|
||||
<A href="#Save">saved</A> or <A href="#Close_Archive_For_Editing">closed for editing</A> the undo/redo stack is
|
||||
cleared.
|
||||
</P>
|
||||
|
||||
@@ -538,7 +525,7 @@
|
||||
The next reverted change which may be re-applied is described by the archive's
|
||||
Redo Change popup menu item. If this action is used and a change is re-applied it may again be reverted by using the
|
||||
<A href="#Undo_Archive_Change">Undo Change</A> action. When the data type archive is
|
||||
<A href="#Save">saved</A> or <A href="#Lock_Archive">closed for editing</A> the undo/redo stack is
|
||||
<A href="#Save">saved</A> or <A href="#Close_Archive_For_Editing">closed for editing</A> the undo/redo stack is
|
||||
cleared.
|
||||
</P>
|
||||
|
||||
@@ -550,7 +537,7 @@
|
||||
<P>Deleting an archive will not only remove the archive from the tree, but will
|
||||
permanently remove it from the filesystem. To delete an archive, right-click on it and
|
||||
select the <I><B>Delete Archive</B></I> action. An archive file must be open for editing
|
||||
before this action will appear (see <A href="#open_for_editing">Opening a Data Type
|
||||
before this action will appear (see <A href="#Open_Archive_For_Editing">Opening a Data Type
|
||||
Archive for Editing</A>).</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
@@ -615,7 +602,7 @@
|
||||
|
||||
<P><IMG alt="" src="help/shared/note.png">
|
||||
The source archive must be editable in order to commit. File archives must be <A href=
|
||||
"data_type_manager_description.htm#open_for_editing">open for editing</A> and project
|
||||
"data_type_manager_description.htm#Open_Archive_For_Editing">open for editing</A> and project
|
||||
archives that are under version control must be checked-out.</P>
|
||||
|
||||
<P><IMG alt="" src="help/shared/note.png">Any data type dependencies for a comitted
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import ghidra.framework.PluggableServiceRegistry;
|
||||
import ghidra.framework.data.ToolStateFactory;
|
||||
import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService;
|
||||
import ghidra.program.database.*;
|
||||
import ghidra.program.database.data.DataTypeArchiveMergeManagerFactory;
|
||||
|
||||
public class FoundationInitializer implements ModuleInitializer {
|
||||
@Override
|
||||
|
||||
+18
-21
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -16,21 +16,20 @@
|
||||
package ghidra.app.merge;
|
||||
|
||||
import ghidra.app.merge.datatypes.DataTypeMergeManager;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.ModalPluginTool;
|
||||
import ghidra.program.model.data.DataTypeManagerDomainObject;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.program.model.listing.DataTypeArchiveChangeSet;
|
||||
|
||||
/**
|
||||
* Top level object that manages each step of the merge/resolve conflicts
|
||||
* process.
|
||||
*/
|
||||
public class DataTypeArchiveMergeManager extends MergeManager {
|
||||
public class DataTypeArchiveMergeManager
|
||||
extends MergeManager<ProjectDataTypeArchive, DataTypeArchiveChangeSet> {
|
||||
|
||||
public DataTypeArchiveMergeManager(DataTypeManagerDomainObject resultDtArchive,
|
||||
DataTypeManagerDomainObject myDtArchive, DataTypeManagerDomainObject originalDtArchive,
|
||||
DataTypeManagerDomainObject latestDtArchive, DataTypeArchiveChangeSet latestChangeSet,
|
||||
public DataTypeArchiveMergeManager(ProjectDataTypeArchive resultDtArchive,
|
||||
ProjectDataTypeArchive myDtArchive, ProjectDataTypeArchive originalDtArchive,
|
||||
ProjectDataTypeArchive latestDtArchive, DataTypeArchiveChangeSet latestChangeSet,
|
||||
DataTypeArchiveChangeSet myChangeSet) {
|
||||
super(resultDtArchive, myDtArchive, originalDtArchive, latestDtArchive, latestChangeSet,
|
||||
myChangeSet);
|
||||
@@ -43,11 +42,8 @@ public class DataTypeArchiveMergeManager extends MergeManager {
|
||||
mergeResolvers = new MergeResolver[1];
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new DataTypeMergeManager(this, (DataTypeManagerDomainObject) resultDomainObject,
|
||||
(DataTypeManagerDomainObject) myDomainObject,
|
||||
(DataTypeManagerDomainObject) originalDomainObject,
|
||||
(DataTypeManagerDomainObject) latestDomainObject,
|
||||
(DataTypeArchiveChangeSet) latestChangeSet, (DataTypeArchiveChangeSet) myChangeSet);
|
||||
new DataTypeMergeManager(this, resultDomainObject, myDomainObject, originalDomainObject,
|
||||
latestDomainObject, latestChangeSet, myChangeSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,16 +54,16 @@ public class DataTypeArchiveMergeManager extends MergeManager {
|
||||
* @return the indicated program version or null if a valid version isn't specified.
|
||||
* @see MergeConstants
|
||||
*/
|
||||
public DataTypeArchive getDataTypeArchive(int version) {
|
||||
public ProjectDataTypeArchive getDataTypeArchive(int version) {
|
||||
switch (version) {
|
||||
case MergeConstants.LATEST:
|
||||
return (DataTypeArchive) latestDomainObject;
|
||||
return latestDomainObject;
|
||||
case MergeConstants.MY:
|
||||
return (DataTypeArchive) myDomainObject;
|
||||
return myDomainObject;
|
||||
case MergeConstants.ORIGINAL:
|
||||
return (DataTypeArchive) originalDomainObject;
|
||||
return originalDomainObject;
|
||||
case MergeConstants.RESULT:
|
||||
return (DataTypeArchive) resultDomainObject;
|
||||
return resultDomainObject;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -75,9 +71,10 @@ public class DataTypeArchiveMergeManager extends MergeManager {
|
||||
|
||||
@Override
|
||||
protected MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool,
|
||||
MergeManager multiUserMergeManager, DomainObject modifiableDomainObject) {
|
||||
MergeManager<ProjectDataTypeArchive, DataTypeArchiveChangeSet> multiUserMergeManager,
|
||||
ProjectDataTypeArchive modifiableDomainObject) {
|
||||
return new DataTypeArchiveMergeManagerPlugin(mergeTool, DataTypeArchiveMergeManager.this,
|
||||
(DataTypeArchive) resultDomainObject);
|
||||
resultDomainObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -21,7 +21,7 @@ import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
|
||||
/**
|
||||
* Plugin that provides a merge component provider for data type archives.
|
||||
@@ -45,7 +45,7 @@ public class DataTypeArchiveMergeManagerPlugin extends MergeManagerPlugin {
|
||||
*/
|
||||
public DataTypeArchiveMergeManagerPlugin(PluginTool tool,
|
||||
DataTypeArchiveMergeManager mergeManager,
|
||||
DataTypeArchive dataTypeArchive) {
|
||||
ProjectDataTypeArchive dataTypeArchive) {
|
||||
super(tool, mergeManager, dataTypeArchive);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,17 +38,20 @@ import help.HelpService;
|
||||
/**
|
||||
* Top level object that manages each step of the merge/resolve conflicts
|
||||
* process.
|
||||
* @param <T> domain object implementation class
|
||||
* @param <C> domain object change set implementation class
|
||||
*/
|
||||
public abstract class MergeManager implements DomainObjectMergeManager {
|
||||
public abstract class MergeManager<T extends DomainObject, C extends DomainObjectChangeSet>
|
||||
implements DomainObjectMergeManager {
|
||||
|
||||
protected MergeResolver[] mergeResolvers;
|
||||
|
||||
protected DomainObject resultDomainObject; // where changes will be merged to
|
||||
protected DomainObject myDomainObject; // source of changes to be applied
|
||||
protected DomainObject originalDomainObject; // original version that was checked out
|
||||
protected DomainObject latestDomainObject; // latest version of the program
|
||||
protected DomainObjectChangeSet latestChangeSet;
|
||||
protected DomainObjectChangeSet myChangeSet;
|
||||
protected T resultDomainObject; // where changes will be merged to
|
||||
protected T myDomainObject; // source of changes to be applied
|
||||
protected T originalDomainObject; // original version that was checked out
|
||||
protected T latestDomainObject; // latest version of the program
|
||||
protected C latestChangeSet;
|
||||
protected C myChangeSet;
|
||||
|
||||
protected MergeManagerPlugin mergePlugin;
|
||||
// protected ListingMergePanelPlugin listingPlugin;
|
||||
@@ -69,9 +72,9 @@ public abstract class MergeManager implements DomainObjectMergeManager {
|
||||
|
||||
// protected boolean isShowingListingMergePanel = false;
|
||||
|
||||
public MergeManager(DomainObject resultDomainObject, DomainObject myDomainObject,
|
||||
DomainObject originalDomainObject, DomainObject latestDomainObject,
|
||||
DomainObjectChangeSet latestChangeSet, DomainObjectChangeSet myChangeSet) {
|
||||
public MergeManager(T resultDomainObject, T myDomainObject,
|
||||
T originalDomainObject, T latestDomainObject,
|
||||
C latestChangeSet, C myChangeSet) {
|
||||
this.resultDomainObject = resultDomainObject;
|
||||
this.myDomainObject = myDomainObject;
|
||||
this.originalDomainObject = originalDomainObject;
|
||||
@@ -95,7 +98,7 @@ public abstract class MergeManager implements DomainObjectMergeManager {
|
||||
* @return the indicated program version or null if a valid version isn't specified.
|
||||
* @see MergeConstants
|
||||
*/
|
||||
public DomainObject getDomainObject(int version) {
|
||||
public T getDomainObject(int version) {
|
||||
switch (version) {
|
||||
case MergeConstants.LATEST:
|
||||
return latestDomainObject;
|
||||
@@ -208,7 +211,7 @@ public abstract class MergeManager implements DomainObjectMergeManager {
|
||||
}
|
||||
|
||||
protected abstract MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool,
|
||||
MergeManager multiUserMergeManager, DomainObject modifiableDomainObject);
|
||||
MergeManager<T, C> multiUserMergeManager, T modifiableDomainObject);
|
||||
|
||||
protected abstract void initializeMerge();
|
||||
|
||||
|
||||
+20
-20
@@ -33,7 +33,6 @@ import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.util.FieldNavigator;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.ModalPluginTool;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.util.PluginException;
|
||||
@@ -50,7 +49,7 @@ import help.HelpService;
|
||||
/**
|
||||
* Top level object that manages each step of the merge/resolve conflicts process.
|
||||
*/
|
||||
public class ProgramMultiUserMergeManager extends MergeManager {
|
||||
public class ProgramMultiUserMergeManager extends MergeManager<Program, ProgramChangeSet> {
|
||||
|
||||
private ListingMergePanelPlugin listingPlugin;
|
||||
private GoToAddressLabelPlugin goToPlugin;
|
||||
@@ -73,10 +72,10 @@ public class ProgramMultiUserMergeManager extends MergeManager {
|
||||
|
||||
@Override
|
||||
protected void createMergeResolvers() {
|
||||
Program resultProgram = (Program) resultDomainObject;
|
||||
Program myProgram = (Program) myDomainObject;
|
||||
Program originalProgram = (Program) originalDomainObject;
|
||||
Program latestProgram = (Program) latestDomainObject;
|
||||
Program resultProgram = resultDomainObject;
|
||||
Program myProgram = myDomainObject;
|
||||
Program originalProgram = originalDomainObject;
|
||||
Program latestProgram = latestDomainObject;
|
||||
// create the merge resolvers
|
||||
int idx = 0;
|
||||
mergeResolvers = new MergeResolver[8];
|
||||
@@ -85,28 +84,28 @@ public class ProgramMultiUserMergeManager extends MergeManager {
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new ProgramTreeMergeManager(this, resultProgram, myProgram, originalProgram,
|
||||
latestProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
latestProgram, latestChangeSet, myChangeSet);
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new DataTypeMergeManager(this, resultProgram, myProgram, originalProgram, latestProgram,
|
||||
(ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
latestChangeSet, myChangeSet);
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new ProgramContextMergeManager(this, resultProgram, originalProgram, latestProgram,
|
||||
myProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
myProgram, latestChangeSet, myChangeSet);
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new FunctionTagMerger(this, resultProgram, originalProgram, latestProgram, myProgram,
|
||||
(ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
latestChangeSet, myChangeSet);
|
||||
|
||||
ListingMergeManager listingMergeManager =
|
||||
new ListingMergeManager(this, resultProgram, originalProgram, latestProgram, myProgram,
|
||||
(ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
latestChangeSet, myChangeSet);
|
||||
mergeResolvers[idx++] = listingMergeManager;
|
||||
|
||||
mergeResolvers[idx++] =
|
||||
new ExternalProgramMerger(this, resultProgram, originalProgram, latestProgram,
|
||||
myProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet);
|
||||
myProgram, latestChangeSet, myChangeSet);
|
||||
|
||||
mergeResolvers[idx++] = new PropertyListMergeManager(this, resultProgram, myProgram,
|
||||
originalProgram, latestProgram);
|
||||
@@ -122,13 +121,13 @@ public class ProgramMultiUserMergeManager extends MergeManager {
|
||||
public Program getProgram(int version) {
|
||||
switch (version) {
|
||||
case MergeConstants.LATEST:
|
||||
return (Program) resultDomainObject;
|
||||
return resultDomainObject;
|
||||
case MergeConstants.MY:
|
||||
return (Program) myDomainObject;
|
||||
return myDomainObject;
|
||||
case MergeConstants.ORIGINAL:
|
||||
return (Program) originalDomainObject;
|
||||
return originalDomainObject;
|
||||
case MergeConstants.RESULT:
|
||||
return (Program) latestDomainObject;
|
||||
return latestDomainObject;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -136,15 +135,16 @@ public class ProgramMultiUserMergeManager extends MergeManager {
|
||||
|
||||
@Override
|
||||
protected MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool,
|
||||
MergeManager multiUserMergeManager, DomainObject modifiableDomainObject) {
|
||||
MergeManager<Program, ProgramChangeSet> multiUserMergeManager,
|
||||
Program modifiableDomainObject) {
|
||||
return new ProgramMergeManagerPlugin(mergeTool, ProgramMultiUserMergeManager.this,
|
||||
(Program) resultDomainObject);
|
||||
resultDomainObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMerge() {
|
||||
mergePanel = new ListingMergePanel(mergeTool, (Program) originalDomainObject,
|
||||
(Program) resultDomainObject, (Program) myDomainObject, (Program) latestDomainObject,
|
||||
mergePanel = new ListingMergePanel(mergeTool, originalDomainObject,
|
||||
resultDomainObject, myDomainObject, latestDomainObject,
|
||||
showListingPanels);
|
||||
mergePanel.removeDomainObjectListener();
|
||||
navigatable = new MergeNavigatable(mergePanel);
|
||||
|
||||
+53
-25
@@ -26,11 +26,14 @@ import org.apache.commons.lang3.Strings;
|
||||
import ghidra.app.merge.*;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.data.DomainObjectMergeManager;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.database.data.DataTypeManagerDB;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.program.model.listing.DataTypeChangeSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -54,7 +57,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
static final int OPTION_ORIGINAL = 2; // Original
|
||||
|
||||
private DomainObjectMergeManager mergeManager;
|
||||
private DataTypeManagerDomainObject[] domainObjects = new DataTypeManagerDomainObject[4];
|
||||
private DomainObject[] domainObjects = new DomainObject[4];
|
||||
private DataTypeManager[] dtms = new DataTypeManager[4];
|
||||
private TaskMonitor currentMonitor;
|
||||
private int originalConflictOption;
|
||||
@@ -93,30 +96,55 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
/**
|
||||
* Manager for merging the data types using the four programs.
|
||||
* @param mergeManager overall merge manager for domain object
|
||||
* @param resultDomainObject the program to be updated with the result of the merge.
|
||||
* @param resultArchive the program to be updated with the result of the merge.
|
||||
* This is the program that will actually get checked in.
|
||||
* @param myDomainObject the program requesting to be checked in.
|
||||
* @param originalDomainObject the program that was checked out.
|
||||
* @param latestDomainObject the latest checked-in version of the program.
|
||||
* @param myArchive the program requesting to be checked in.
|
||||
* @param originalArchive the program that was checked out.
|
||||
* @param latestArchive the latest checked-in version of the program.
|
||||
* @param latestChanges the address set of changes between original and latest versioned program.
|
||||
* @param myChanges the address set of changes between original and my modified program.
|
||||
*/
|
||||
public DataTypeMergeManager(DomainObjectMergeManager mergeManager,
|
||||
DataTypeManagerDomainObject resultDomainObject,
|
||||
DataTypeManagerDomainObject myDomainObject,
|
||||
DataTypeManagerDomainObject originalDomainObject,
|
||||
DataTypeManagerDomainObject latestDomainObject, DataTypeChangeSet latestChanges,
|
||||
ProjectDataTypeArchive resultArchive,
|
||||
ProjectDataTypeArchive myArchive,
|
||||
ProjectDataTypeArchive originalArchive,
|
||||
ProjectDataTypeArchive latestArchive,
|
||||
DataTypeChangeSet latestChanges,
|
||||
DataTypeChangeSet myChanges) {
|
||||
this.mergeManager = mergeManager;
|
||||
domainObjects[RESULT] = resultDomainObject;
|
||||
domainObjects[ORIGINAL] = originalDomainObject;
|
||||
domainObjects[LATEST] = latestDomainObject;
|
||||
domainObjects[MY] = myDomainObject;
|
||||
dtms[RESULT] = resultDomainObject.getDataTypeManager();
|
||||
dtms[ORIGINAL] = originalDomainObject.getDataTypeManager();
|
||||
dtms[LATEST] = latestDomainObject.getDataTypeManager();
|
||||
dtms[MY] = myDomainObject.getDataTypeManager();
|
||||
|
||||
this.mergeManager = mergeManager;
|
||||
domainObjects[RESULT] = resultArchive;
|
||||
domainObjects[ORIGINAL] = originalArchive;
|
||||
domainObjects[LATEST] = latestArchive;
|
||||
domainObjects[MY] = myArchive;
|
||||
dtms[RESULT] = resultArchive.getDataTypeManager();
|
||||
dtms[ORIGINAL] = originalArchive.getDataTypeManager();
|
||||
dtms[LATEST] = latestArchive.getDataTypeManager();
|
||||
dtms[MY] = myArchive.getDataTypeManager();
|
||||
init(latestChanges, myChanges);
|
||||
}
|
||||
|
||||
public DataTypeMergeManager(DomainObjectMergeManager mergeManager,
|
||||
Program resultProgram,
|
||||
Program myProgram,
|
||||
Program originalProgram,
|
||||
Program latestProgram,
|
||||
DataTypeChangeSet latestChanges,
|
||||
DataTypeChangeSet myChanges) {
|
||||
|
||||
this.mergeManager = mergeManager;
|
||||
domainObjects[RESULT] = resultProgram;
|
||||
domainObjects[ORIGINAL] = originalProgram;
|
||||
domainObjects[LATEST] = latestProgram;
|
||||
domainObjects[MY] = myProgram;
|
||||
dtms[RESULT] = resultProgram.getDataTypeManager();
|
||||
dtms[ORIGINAL] = originalProgram.getDataTypeManager();
|
||||
dtms[LATEST] = latestProgram.getDataTypeManager();
|
||||
dtms[MY] = myProgram.getDataTypeManager();
|
||||
init(latestChanges, myChanges);
|
||||
}
|
||||
|
||||
private void init(DataTypeChangeSet latestChanges, DataTypeChangeSet myChanges) {
|
||||
totalConflictCount = 0;
|
||||
setupSourceArchiveChanges(latestChanges, myChanges);
|
||||
setupDataTypeChanges(latestChanges, myChanges);
|
||||
@@ -1315,7 +1343,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
try {
|
||||
resultComp =
|
||||
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
displayError(destStruct, e);
|
||||
@@ -1338,14 +1366,14 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
// If I have compDt, it should now be from result DTM.
|
||||
resultComp =
|
||||
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
|
||||
comment);
|
||||
comment);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
comment =
|
||||
buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment);
|
||||
resultComp =
|
||||
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
sourceComp.getFieldName(), comment);
|
||||
if (e.getCause() instanceof DataTypeDependencyException) {
|
||||
badIdDtMsgs.put(dtId, e.getMessage());
|
||||
}
|
||||
@@ -1364,8 +1392,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
try {
|
||||
resultComp = destStruct
|
||||
.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
|
||||
bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
displayError(destStruct, e);
|
||||
@@ -1376,7 +1404,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
try {
|
||||
resultComp =
|
||||
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException exc) {
|
||||
throw new RuntimeException(exc); // unexpected
|
||||
@@ -1429,7 +1457,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment);
|
||||
resultComp =
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
sourceComp.getLength(), sourceComp.getFieldName(), comment);
|
||||
sourceComp.getLength(), sourceComp.getFieldName(), comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -33,9 +33,9 @@ import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.OptionType;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.Msg;
|
||||
@@ -132,11 +132,11 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
options.registerOption(OPTION_NAME_GDT_FILEPATH, OptionType.FILE_TYPE, null, null,
|
||||
OPTION_DESCRIPTION_GDT_FILEPATH,
|
||||
() -> new FileChooserEditor(FileDataTypeManager.GDT_FILEFILTER));
|
||||
() -> new FileChooserEditor(FileDataTypeArchive.GDT_FILEFILTER));
|
||||
options.registerOption(OPTION_NAME_PROJECT_PATH, OptionType.STRING_TYPE, null, null,
|
||||
OPTION_DESCRIPTION_PROJECT_PATH,
|
||||
() -> new ProjectPathChooserEditor("Choose Data Type Archive",
|
||||
new DefaultDomainFileFilter(DataTypeArchive.class, false)));
|
||||
new DefaultDomainFileFilter(ProjectDataTypeArchive.class, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -293,7 +293,7 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||
log.appendMsg("Missing project archive: %s".formatted(filename));
|
||||
return List.of();
|
||||
}
|
||||
if (!DataTypeArchive.class.isAssignableFrom(gdtDomainFile.getDomainObjectClass())) {
|
||||
if (!ProjectDataTypeArchive.class.isAssignableFrom(gdtDomainFile.getDomainObjectClass())) {
|
||||
log.appendMsg("Bad project file type: %s".formatted(filename));
|
||||
return List.of();
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,14 +21,14 @@ import java.util.Set;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DefaultDataTypeArchiveService;
|
||||
import ghidra.app.plugin.core.datamgr.archive.BasicDataTypeArchiveService;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
// FIXME!! TESTING
|
||||
public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
|
||||
public class DefaultDataTypeManagerService extends BasicDataTypeArchiveService
|
||||
implements DataTypeManagerService {
|
||||
|
||||
// TODO: This implementation needs to be consolidated with the tool-based service in
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/* ###
|
||||
* 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.compositeeditor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveDB;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* DataTypeAchive used to edit composites (unions or structures)
|
||||
*
|
||||
* @param <T> The specific type of composite
|
||||
*/
|
||||
public class CompositeEditorDtArchiveDB<T extends Composite> extends DataTypeArchiveDB {
|
||||
private int transactionId;
|
||||
|
||||
/**
|
||||
* Special constructor for stack editor. We create a transaction that is always open for the
|
||||
* life of this archive so that undo/redo is not available.
|
||||
* @param originalDTM the data type manager this archive is shadowing.
|
||||
* @throws IOException if an {@link IOException} occurs initializing the database.
|
||||
*/
|
||||
CompositeEditorDtArchiveDB(DataTypeManager originalDTM) throws IOException {
|
||||
this(originalDTM, null, null, null);
|
||||
|
||||
// This prevents undo/redo by always have a transaction open during its full lifecycle.
|
||||
transactionId = startTransaction("Composite Edit");
|
||||
}
|
||||
|
||||
public CompositeEditorDtArchiveDB(T originalComposite, Callback changeCallback,
|
||||
Callback restoredCallback) throws IOException {
|
||||
this(originalComposite.getDataTypeManager(), originalComposite, changeCallback,
|
||||
restoredCallback);
|
||||
}
|
||||
|
||||
protected CompositeEditorDtArchiveDB(DataTypeManager originalDTM, T originalComposite,
|
||||
Callback changeCallback,
|
||||
Callback restoredCallback) throws IOException {
|
||||
super(new DBHandle(), originalDTM.getName(), new Object());
|
||||
CompositeViewerDataTypeManager<T> dtm = getDataTypeManager();
|
||||
withTransaction("Setup for Edit", () -> {
|
||||
dtm.intialize(originalDTM, originalComposite, changeCallback, restoredCallback);
|
||||
});
|
||||
clearUndo();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public CompositeViewerDataTypeManager<T> getDataTypeManager() {
|
||||
return (CompositeViewerDataTypeManager<T>) super.getDataTypeManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompositeViewerDataTypeManager<T> createDataTypeManager(DBHandle handle,
|
||||
OpenMode openMode, TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
|
||||
return new CompositeViewerDataTypeManager<>(handle, openMode, this, lock, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTemporary() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Temporary Data Type Archive For Composite Editing";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isArchitectureChangeAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close() {
|
||||
if (transactionId != 0) {
|
||||
endTransaction(transactionId, changed);
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getArchiveType() {
|
||||
return ArchiveType.TEMPORARY;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-7
@@ -100,9 +100,10 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
|
||||
originalDataTypePath = originalComposite.getDataTypePath();
|
||||
currentName = originalComposite.getName();
|
||||
|
||||
// Use temporary standalone view datatype manager
|
||||
viewDTM = new CompositeViewerDataTypeManager<>(viewDTM.getName(),
|
||||
viewDTM.getResolvedViewComposite(), this::componentEdited, this::restoreEditor);
|
||||
// Use temporary view datatype manager
|
||||
T composite = viewDTM.getResolvedViewComposite();
|
||||
viewDTM = CompositeViewerDataTypeManager.createUndoableInstance(composite, this::componentEdited,
|
||||
this::restoreEditor);
|
||||
|
||||
viewComposite = viewDTM.getResolvedViewComposite();
|
||||
|
||||
@@ -225,10 +226,9 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
|
||||
viewDTM = null;
|
||||
}
|
||||
|
||||
// Use temporary standalone view datatype manager
|
||||
viewDTM =
|
||||
new CompositeViewerDataTypeManager<>(originalComposite.getDataTypeManager().getName(),
|
||||
originalComposite, this::componentEdited, this::restoreEditor);
|
||||
// Use temporary stand-alone view datatype archive
|
||||
viewDTM = CompositeViewerDataTypeManager.createUndoableInstance(originalComposite,
|
||||
this::componentEdited, this::restoreEditor);
|
||||
|
||||
viewComposite = viewDTM.getResolvedViewComposite();
|
||||
|
||||
|
||||
+63
-58
@@ -21,13 +21,17 @@ import java.util.TreeSet;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.util.ErrorHandler;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.framework.model.RuntimeIOException;
|
||||
import ghidra.program.database.DbObject;
|
||||
import ghidra.program.database.data.ArchiveDataTypeManagerDB;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.ProgramArchitecture;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utility.function.Callback;
|
||||
|
||||
@@ -38,25 +42,24 @@ import utility.function.Callback;
|
||||
* the editor.
|
||||
* @param <T> Specific {@link Composite} type being managed
|
||||
*/
|
||||
public class CompositeViewerDataTypeManager<T extends Composite> extends StandAloneDataTypeManager
|
||||
public class CompositeViewerDataTypeManager<T extends Composite> extends ArchiveDataTypeManagerDB
|
||||
implements ErrorHandler {
|
||||
|
||||
/**
|
||||
* The data type manager for original composite data type being edited.
|
||||
* This is where the edited datatype will be written back to.
|
||||
*/
|
||||
private final DataTypeManager originalDTM;
|
||||
private final T originalComposite; // may be null if not resolved into this DTM
|
||||
private final T viewComposite; // may be null if not resolved into this DTM
|
||||
private DataTypeManager originalDTM;
|
||||
private T originalComposite; // may be null if not resolved into this DTM
|
||||
private T viewComposite; // may be null if not resolved into this DTM
|
||||
|
||||
// Database-backed datatype ID map, view to/from original DTM
|
||||
// This is needed to account for datatype use and ID alterations across undo/redo
|
||||
private final IDMapDB dataTypeIDMap;
|
||||
private IDMapDB dataTypeIDMap;
|
||||
|
||||
// Editor transaction use only - undo/redo not supported if restoreCallback is null
|
||||
private Callback restoredCallback;
|
||||
private Callback changeCallback;
|
||||
private int transactionId = 0;
|
||||
private boolean dataTypeChanged;
|
||||
|
||||
// Modification count used to signal optional clearing of undo/redo stack at the end of a
|
||||
@@ -66,49 +69,51 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
// datatype IDs to be checked as orphaned.
|
||||
// NOTE: Orphan removal can only be done when this DTM actively manages the viewComposite
|
||||
private TreeSet<Long> orphanIds = new TreeSet<>();
|
||||
private int transactionCount;
|
||||
|
||||
/**
|
||||
* Creates a data type manager that the composite editor will use internally for managing
|
||||
* dependencies without resolving the actual composite being edited. A single transaction
|
||||
* will be started with this instantiation and held open until this instance is closed.
|
||||
* Undo/redo and datatype pruning is not be supported.
|
||||
* @param rootName the root name for this data type manager (usually the program name).
|
||||
* @param originalDTM the original data type manager.
|
||||
*/
|
||||
public CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM) {
|
||||
this(rootName, originalDTM, null, null, null);
|
||||
transactionId = startTransaction("Composite Edit");
|
||||
public static <T extends Composite> CompositeViewerDataTypeManager<T> createNonUndoableInstance(
|
||||
DataTypeManager originalDTM) {
|
||||
try {
|
||||
CompositeEditorDtArchiveDB<T> archive =
|
||||
new CompositeEditorDtArchiveDB<T>(originalDTM);
|
||||
|
||||
return archive.getDataTypeManager();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a data type manager that the structure editor will use internally for managing a
|
||||
* structure being edited and its dependencies.
|
||||
* @param rootName the root name for this data type manager (usually the program name).
|
||||
* @param originalComposite the original composite data type that is being edited.
|
||||
* @param changeCallback Callback will be invoked when any change is made to the view composite.
|
||||
* @param restoredCallback Callback will be invoked following any undo/redo.
|
||||
*/
|
||||
public CompositeViewerDataTypeManager(String rootName, T originalComposite,
|
||||
Callback changeCallback, Callback restoredCallback) {
|
||||
this(rootName, originalComposite.getDataTypeManager(), originalComposite, changeCallback,
|
||||
restoredCallback);
|
||||
public static <T extends Composite> CompositeViewerDataTypeManager<T> createUndoableInstance(
|
||||
T originalComposite, Callback changeCallback,
|
||||
Callback restoredCallback) {
|
||||
try {
|
||||
CompositeEditorDtArchiveDB<T> archive =
|
||||
new CompositeEditorDtArchiveDB<T>(originalComposite, changeCallback,
|
||||
restoredCallback);
|
||||
return archive.getDataTypeManager();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param rootName the root name for this data type manager (usually the program name).
|
||||
* @param originalDTM the original datatype manager
|
||||
* @param originalComposite the original composite data type that is being edited. (may be null)
|
||||
* @param changeCallback Callback will be invoked when any change is made to the view composite.
|
||||
* @param restoredCallback Callback will be invoked following any undo/redo.
|
||||
*/
|
||||
private CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM,
|
||||
T originalComposite, Callback changeCallback, Callback restoredCallback) {
|
||||
super(rootName, originalDTM.getDataOrganization());
|
||||
this.originalDTM = originalDTM;
|
||||
this.originalComposite = originalComposite;
|
||||
this.changeCallback = changeCallback;
|
||||
this.restoredCallback = restoredCallback;
|
||||
CompositeViewerDataTypeManager(DBHandle handle,
|
||||
OpenMode openMode, ErrorHandler errHandler, Lock lock, TaskMonitor monitor)
|
||||
throws CancelledException, VersionException, IOException {
|
||||
super(handle, openMode, errHandler, lock, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompositeEditorDtArchiveDB<?> getDataStore() {
|
||||
return (CompositeEditorDtArchiveDB<?>) super.getDataStore();
|
||||
}
|
||||
|
||||
void intialize(DataTypeManager dtm, T composite, Callback changeCb, Callback restoredCb) {
|
||||
this.originalDTM = dtm;
|
||||
this.originalComposite = composite;
|
||||
this.changeCallback = changeCb;
|
||||
this.restoredCallback = restoredCb;
|
||||
|
||||
int txId = startTransaction("Setup for Edit");
|
||||
try {
|
||||
@@ -120,6 +125,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
endTransaction(txId, true);
|
||||
}
|
||||
clearUndo();
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -150,10 +156,8 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
return dbHandle.getModCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void clearUndo() {
|
||||
// Exposes method for test use
|
||||
super.clearUndo();
|
||||
synchronized void clearUndo() {
|
||||
archive.clearUndo();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,10 +201,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (transactionId != 0) {
|
||||
super.endTransaction(transactionId, true);
|
||||
}
|
||||
super.close();
|
||||
getDataStore().close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,10 +361,16 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int startTransaction(String description) {
|
||||
transactionCount++;
|
||||
return super.startTransaction(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean endTransaction(int transactionID, boolean commit) {
|
||||
|
||||
if (viewComposite != null && getTransactionCount() == 1) {
|
||||
if (viewComposite != null && transactionCount == 1) {
|
||||
// Perform orphan removal only at the end of the outer-most transaction
|
||||
synchronized (orphanIds) {
|
||||
checkOrphansForRemoval(false);
|
||||
@@ -371,6 +378,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
}
|
||||
|
||||
boolean committed = super.endTransaction(transactionID, commit);
|
||||
transactionCount--;
|
||||
|
||||
if (!isTransactionActive() && flattenModCount != -1) {
|
||||
if (flattenModCount != dbHandle.getModCount()) {
|
||||
@@ -382,9 +390,6 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
||||
|
||||
if (committed && dataTypeChanged && changeCallback != null) {
|
||||
Swing.runLater(() -> changeCallback.call());
|
||||
}
|
||||
|
||||
if (getTransactionCount() == 0) {
|
||||
dataTypeChanged = false;
|
||||
}
|
||||
|
||||
|
||||
+38
-30
@@ -17,15 +17,18 @@ package ghidra.app.plugin.core.cparser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.dialogs.MultiLineMessageDialog;
|
||||
import ghidra.app.util.cparser.C.CParserUtils.CParseResults;
|
||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.dtarchive.DataTypeStore;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateFileException;
|
||||
import ghidra.util.task.Task;
|
||||
@@ -39,16 +42,16 @@ import ghidra.util.task.TaskMonitor;
|
||||
class CParserTask extends Task {
|
||||
|
||||
private CParserPlugin plugin;
|
||||
|
||||
|
||||
private String[] filenames;
|
||||
private String[] includePaths;
|
||||
|
||||
|
||||
private String options;
|
||||
|
||||
|
||||
// Language and Compiler Spec IDs valid only for new dataFileName use
|
||||
private String languageId;
|
||||
private String compilerSpecId;
|
||||
|
||||
|
||||
// Either dataTypeManager or dataFileName must be set, but not both
|
||||
private final DataTypeManager dataTypeManager; // specified for an existing DataTypeManager
|
||||
private final File dataFile; // specified for a new file
|
||||
@@ -61,6 +64,7 @@ class CParserTask extends Task {
|
||||
*/
|
||||
CParserTask(CParserPlugin plugin, String dataFileName) {
|
||||
super("Parsing C Files", true, false, false);
|
||||
Objects.requireNonNull(dataFileName, "Target datatype archive filename required");
|
||||
dataTypeManager = null;
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(dataFileName);
|
||||
@@ -77,6 +81,7 @@ class CParserTask extends Task {
|
||||
*/
|
||||
public CParserTask(CParserPlugin plugin, DataTypeManager dataTypeManager) {
|
||||
super("Parsing C Files", true, false, false);
|
||||
Objects.requireNonNull(dataTypeManager, "Target datatype manager required");
|
||||
dataFile = null;
|
||||
this.plugin = plugin;
|
||||
this.dataTypeManager = dataTypeManager;
|
||||
@@ -101,7 +106,7 @@ class CParserTask extends Task {
|
||||
this.languageId = languageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the compiler spec ID to be used. This ID must be defined for the specified language.
|
||||
*
|
||||
@@ -121,17 +126,17 @@ class CParserTask extends Task {
|
||||
this.compilerSpecId = compilerSpecId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CParserTask setIncludePaths(String includePaths[]) {
|
||||
this.includePaths = includePaths.clone();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CParserTask setFileNames(String names[]) {
|
||||
this.filenames = names.clone();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CParserTask setOptions(String options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
@@ -158,16 +163,16 @@ class CParserTask extends Task {
|
||||
return msg;
|
||||
}
|
||||
|
||||
private String getParseDestination(DataTypeManager dtMgr) {
|
||||
private String getParseDestination(DataTypeStore archive) {
|
||||
String parseDest = "";
|
||||
if (dtMgr instanceof ProgramDataTypeManager) {
|
||||
parseDest = "Program " + dtMgr.getName();
|
||||
if (archive instanceof Program) {
|
||||
parseDest = "Program " + archive.getName();
|
||||
}
|
||||
else if (dtMgr instanceof FileDataTypeManager fileDtm) {
|
||||
parseDest = "Archive File: " + fileDtm.getFilename();
|
||||
else if (archive instanceof FileDataTypeArchive fileArchive) {
|
||||
parseDest = "Archive File: " + fileArchive.getPath();
|
||||
}
|
||||
else {
|
||||
parseDest = dtMgr.getName();
|
||||
parseDest = archive.getName();
|
||||
}
|
||||
return parseDest;
|
||||
}
|
||||
@@ -175,7 +180,7 @@ class CParserTask extends Task {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
|
||||
FileDataTypeManager fileDtMgr = null;
|
||||
FileDataTypeArchive dataFileArchive = null;
|
||||
if (dataFile != null) {
|
||||
try {
|
||||
if ((languageId != null) != (compilerSpecId != null)) {
|
||||
@@ -184,8 +189,8 @@ class CParserTask extends Task {
|
||||
compilerSpecId);
|
||||
return;
|
||||
}
|
||||
fileDtMgr =
|
||||
FileDataTypeManager.createFileArchive(dataFile, languageId, compilerSpecId);
|
||||
dataFileArchive = DataTypeArchiveFactory.createFileArchive(dataFile, languageId,
|
||||
compilerSpecId, this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, plugin.getDialog().getComponent(), "Archive Failure",
|
||||
@@ -194,10 +199,12 @@ class CParserTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
DataTypeManager dtMgr = fileDtMgr != null ? fileDtMgr : dataTypeManager;
|
||||
DataTypeManager dtMgr =
|
||||
dataFileArchive != null ? dataFileArchive.getDataTypeManager() : dataTypeManager;
|
||||
|
||||
int initialDtCount = dtMgr.getDataTypeCount(true);
|
||||
|
||||
String archiveDetination = getParseDestination(dtMgr.getDataStore());
|
||||
try {
|
||||
|
||||
CParseResults results = plugin.parse(filenames, includePaths, options, dtMgr, monitor);
|
||||
@@ -205,10 +212,10 @@ class CParserTask extends Task {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
if (fileDtMgr != null && dtMgr.getDataTypeCount(true) != 0) {
|
||||
if (dataFileArchive != null && dtMgr.getDataTypeCount(true) != 0) {
|
||||
// If archive created - save to file
|
||||
try {
|
||||
fileDtMgr.save();
|
||||
dataFileArchive.save(null, monitor);
|
||||
}
|
||||
catch (DuplicateFileException e) {
|
||||
Msg.showError(this, plugin.getDialog().getComponent(),
|
||||
@@ -228,7 +235,7 @@ class CParserTask extends Task {
|
||||
if (!results.successful()) {
|
||||
MultiLineMessageDialog.showModalMessageDialog(
|
||||
plugin.getDialog().getComponent(), "C-Parse Failed",
|
||||
"Failed to parse header file(s) to " + getParseDestination(dtMgr),
|
||||
"Failed to parse header file(s) to " + archiveDetination,
|
||||
plugin.getFormattedParseMessage(msg),
|
||||
MultiLineMessageDialog.INFORMATION_MESSAGE);
|
||||
}
|
||||
@@ -236,7 +243,7 @@ class CParserTask extends Task {
|
||||
MultiLineMessageDialog.showModalMessageDialog(
|
||||
plugin.getDialog().getComponent(),
|
||||
"C-Parse Completed",
|
||||
"Successfully parsed header file(s) to " + getParseDestination(dtMgr),
|
||||
"Successfully parsed header file(s) to " + archiveDetination,
|
||||
plugin.getFormattedParseMessage(msg),
|
||||
MultiLineMessageDialog.INFORMATION_MESSAGE);
|
||||
}
|
||||
@@ -247,7 +254,7 @@ class CParserTask extends Task {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(),
|
||||
"C-Parse Failed",
|
||||
"Failed to parse header file(s) to " + getParseDestination(dtMgr),
|
||||
"Failed to parse header file(s) to " + archiveDetination,
|
||||
plugin.getFormattedParseMessage(errMsg),
|
||||
MultiLineMessageDialog.ERROR_MESSAGE);
|
||||
});
|
||||
@@ -257,7 +264,7 @@ class CParserTask extends Task {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(),
|
||||
"C-PreProcessor Parse Failed",
|
||||
"Failed to parse header file(s) to " + getParseDestination(dtMgr),
|
||||
"Failed to parse header file(s) to " + archiveDetination,
|
||||
plugin.getFormattedParseMessage(errMsg),
|
||||
MultiLineMessageDialog.ERROR_MESSAGE);
|
||||
});
|
||||
@@ -270,15 +277,16 @@ class CParserTask extends Task {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(),
|
||||
"Error During C-Parse",
|
||||
"Failed to parse header file(s) to " + getParseDestination(dtMgr),
|
||||
"Failed to parse header file(s) to " + archiveDetination,
|
||||
plugin.getFormattedParseMessage(errMsg),
|
||||
MultiLineMessageDialog.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
if (fileDtMgr != null) {
|
||||
boolean deleteFile = fileDtMgr.getDataTypeCount(true) == 0;
|
||||
fileDtMgr.close();
|
||||
if (dataFileArchive != null) {
|
||||
boolean deleteFile =
|
||||
dataFileArchive.getDataTypeManager().getDataTypeCount(true) == 0;
|
||||
dataFileArchive.release(this);
|
||||
if (deleteFile) {
|
||||
dataFile.delete();
|
||||
}
|
||||
|
||||
+17
-17
@@ -46,7 +46,7 @@ import ghidra.framework.Application;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.util.HelpLocation;
|
||||
@@ -150,7 +150,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
void writeState(SaveState saveState) {
|
||||
// Get the current state if the dialog has been displayed
|
||||
if (!initialBuild) {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
|
||||
currentProfileName = item.file.getName();
|
||||
userDefined = item.isUserDefined;
|
||||
@@ -172,7 +172,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
if (initialBuild) {
|
||||
return;
|
||||
}
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
if (item.isChanged) {
|
||||
processItemChanged(item);
|
||||
}
|
||||
@@ -255,7 +255,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
});
|
||||
|
||||
tableListener = e -> {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
item.isChanged = !initialBuild;
|
||||
notifyContextChanged();
|
||||
};
|
||||
@@ -270,7 +270,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
new ExtensionFileFilter(new String[] { "h" }, "C Header Files"));
|
||||
|
||||
parsePathTableListener = e -> {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
item.isChanged = !initialBuild;
|
||||
notifyContextChanged();
|
||||
pathPanel.getTable().repaint();
|
||||
@@ -485,7 +485,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
private void itemChanged() {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
@@ -497,12 +497,12 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
saveAction = new DockingAction("Save Profile", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
save((ComboBoxItem) comboBox.getSelectedItem());
|
||||
save(comboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
return item.isChanged && item.isUserDefined;
|
||||
}
|
||||
};
|
||||
@@ -516,7 +516,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
saveAsAction = new DockingAction("Save Profile As", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
saveAs((ComboBoxItem) comboBox.getSelectedItem());
|
||||
saveAs(comboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -577,7 +577,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
return item.isUserDefined;
|
||||
}
|
||||
};
|
||||
@@ -590,7 +590,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
if (item.isChanged) {
|
||||
processItemChanged(item);
|
||||
}
|
||||
@@ -610,7 +610,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
private void clear() {
|
||||
pathPanel.clear();
|
||||
parseOptionsField.setText("");
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
item.isChanged = true;
|
||||
}
|
||||
|
||||
@@ -681,7 +681,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
tableModel.removeTableModelListener(tableListener);
|
||||
parsePathTableModel.removeTableModelListener(parsePathTableListener);
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
item.isChanged = false;
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
@@ -817,7 +817,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
private void delete() {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
if (item.isUserDefined) {
|
||||
if (OptionDialog.showOptionDialog(getComponent(), "Delete Profile?",
|
||||
"Are you sure you want to delete profile " + item.getName(), "Delete",
|
||||
@@ -944,8 +944,8 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
String name = file.getName();
|
||||
if (!file.getName().endsWith(FileDataTypeManager.SUFFIX)) {
|
||||
file = new File(file.getParentFile(), name + FileDataTypeManager.SUFFIX);
|
||||
if (!file.getName().endsWith(FileDataTypeArchive.SUFFIX)) {
|
||||
file = new File(file.getParentFile(), name + FileDataTypeArchive.SUFFIX);
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
@@ -1086,7 +1086,7 @@ class ParseDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
ComboBoxItem getCurrentItem() {
|
||||
ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
|
||||
ComboBoxItem item = comboBox.getSelectedItem();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
+572
File diff suppressed because it is too large
Load Diff
+11
-13
@@ -1,37 +1,35 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.archive;
|
||||
|
||||
import ghidra.framework.GenericRunInfo;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import ghidra.framework.GenericRunInfo;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
|
||||
public class ArchiveFileChooser extends GhidraFileChooser {
|
||||
|
||||
public ArchiveFileChooser(Component component) {
|
||||
super(component);
|
||||
|
||||
setFileFilter(new ExtensionFileFilter(new String[] { FileDataTypeManager.EXTENSION },
|
||||
setFileFilter(new ExtensionFileFilter(new String[] { FileDataTypeArchive.EXTENSION },
|
||||
"Ghidra Data Type Files"));
|
||||
setApproveButtonText("Save As");
|
||||
setApproveButtonToolTipText("Save As");
|
||||
@@ -52,7 +50,7 @@ public class ArchiveFileChooser extends GhidraFileChooser {
|
||||
}
|
||||
setCurrentDirectory(projectDirectory);
|
||||
|
||||
String suggestedName = suggestedFileName + FileDataTypeManager.SUFFIX;
|
||||
String suggestedName = suggestedFileName + FileDataTypeArchive.SUFFIX;
|
||||
setSelectedFile(new File(projectDirectory, suggestedName));
|
||||
|
||||
File file = getSelectedFile();
|
||||
@@ -68,10 +66,10 @@ public class ArchiveFileChooser extends GhidraFileChooser {
|
||||
|
||||
private File fixFilenameSuffix(File file) {
|
||||
String filename = file.getName();
|
||||
if (filename.endsWith(FileDataTypeManager.SUFFIX)) {
|
||||
if (filename.endsWith(FileDataTypeArchive.SUFFIX)) {
|
||||
return file;
|
||||
}
|
||||
filename += FileDataTypeManager.SUFFIX;
|
||||
filename += FileDataTypeArchive.SUFFIX;
|
||||
return new File(file.getParentFile(), filename);
|
||||
}
|
||||
}
|
||||
+826
File diff suppressed because it is too large
Load Diff
+73
@@ -0,0 +1,73 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.InvalidArchive;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.DataTypeStore;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Interface for notifications when archives change in the archive manager
|
||||
*/
|
||||
public interface ArchiveManagerListener {
|
||||
/**
|
||||
* Called when a new Archive is opened.
|
||||
* @param archive the new archive that was opened.
|
||||
*/
|
||||
public void archiveOpened(PersistentDataTypeArchive archive);
|
||||
|
||||
/**
|
||||
* Called when an archive is closed.
|
||||
* @param archive the archive that was closed.
|
||||
*/
|
||||
public void archiveClosed(PersistentDataTypeArchive archive);
|
||||
|
||||
/**
|
||||
* Called when a new program is activated
|
||||
* @param program the program that is activated
|
||||
*/
|
||||
public void programOpened(Program program);
|
||||
|
||||
/**
|
||||
* Called when the current program is deactivated
|
||||
* @param program the program that is being deactivated
|
||||
*/
|
||||
public void programClosed(Program program);
|
||||
|
||||
/**
|
||||
* Called when the tool attempts to open a file or project archive that is referenced by
|
||||
* a program, but can't be found. This creates a object that actions can operate on to
|
||||
* either attempt to find it or use it to remove the references from a program.
|
||||
* @param invalidArchive the InvalidArchive object that describes what the program was trying
|
||||
* to open.
|
||||
*/
|
||||
public void invalidArchiveAdded(InvalidArchive invalidArchive);
|
||||
|
||||
/**
|
||||
* Called when an invalid archive is removed from the tool.
|
||||
* @param invalidArchive the invalid archive object that was removed from the tool
|
||||
*/
|
||||
public void invalidArchiveRemoved(InvalidArchive invalidArchive);
|
||||
|
||||
/**
|
||||
* Notification that a change to the datatypeStore has occurred. Could be a datatype or
|
||||
* category was added, changed, or deleted. Could also mean that the changes were saved to
|
||||
* the backing store or the that a save as operation occurred.
|
||||
* @param dataTypeStore The store whose state changed
|
||||
*/
|
||||
public void stateChanged(DataTypeStore dataTypeStore);
|
||||
}
|
||||
+61
-62
@@ -4,16 +4,16 @@
|
||||
* 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.framework.main.datatree;
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -22,15 +22,19 @@ import java.util.List;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTreeState;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
import ghidra.app.services.Recover;
|
||||
import ghidra.app.services.Upgrade;
|
||||
import ghidra.framework.client.ClientUtil;
|
||||
import ghidra.framework.main.SaveDataDialog;
|
||||
import ghidra.framework.main.datatable.DomainFileContext;
|
||||
import ghidra.framework.main.datatree.UndoActionDialog;
|
||||
import ghidra.framework.main.projectdata.actions.VersionControlAction;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
@@ -39,24 +43,21 @@ import ghidra.util.task.TaskMonitor;
|
||||
/**
|
||||
* Action to undo checkouts for domain files in the repository.
|
||||
*/
|
||||
public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionControlAction {
|
||||
public class ArchiveUndoCheckoutTask extends VersionControlAction {
|
||||
|
||||
private static final Icon ICON =
|
||||
new GIcon("icon.base.util.datatree.version.control.archive.dt.checkout.undo");
|
||||
|
||||
private DataTypeManagerPlugin dtmPlugin;
|
||||
private ArchiveProvider archiveProvider;
|
||||
|
||||
/**
|
||||
* Creates an action to undo checkouts for domain files in the repository.
|
||||
* @param plugin the plug-in that owns this action.
|
||||
* @param provider provides a list of domain files to be affected by this action.
|
||||
*/
|
||||
public VersionControlDataTypeArchiveUndoCheckoutAction(DataTypeManagerPlugin plugin,
|
||||
ArchiveProvider provider) {
|
||||
public ArchiveUndoCheckoutTask(DataTypeManagerPlugin plugin) {
|
||||
super("UndoCheckOut", plugin.getName(), plugin.getTool());
|
||||
this.dtmPlugin = plugin;
|
||||
this.archiveProvider = provider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Undo Checkout" }, ICON, GROUP));
|
||||
setDescription("Undo checkout");
|
||||
@@ -65,19 +66,22 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
|
||||
@Override
|
||||
public void actionPerformed(DomainFileContext context) {
|
||||
undoCheckOut();
|
||||
undoCheckOut((DataTypesActionContext) context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(DomainFileContext context) {
|
||||
if (!(context instanceof DataTypesActionContext dataTypesContext)) {
|
||||
return false;
|
||||
}
|
||||
if (isFileSystemBusy()) {
|
||||
return false; // don't block; we should get called again later
|
||||
}
|
||||
|
||||
List<DomainFile> domainFiles = context.getSelectedFiles();
|
||||
for (DomainFile domainFile : domainFiles) {
|
||||
if (domainFile.isCheckedOut()) {
|
||||
return true; // At least one checked out file selected.
|
||||
List<ProjectDataTypeArchive> archives = dataTypesContext.getSelectedProjectArchives();
|
||||
for (ProjectDataTypeArchive archive : archives) {
|
||||
if (archive.getDomainFile().isCheckedOut()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -86,19 +90,19 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
/**
|
||||
* Gets the domain files from the provider and then undoes the checkout on any that are
|
||||
* checked out.
|
||||
* @param context
|
||||
*/
|
||||
protected void undoCheckOut() {
|
||||
protected void undoCheckOut(DataTypesActionContext context) {
|
||||
if (!checkRepositoryConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeEditorsForUndoCheckOut();
|
||||
List<ProjectDataTypeArchive> archives = context.getSelectedProjectArchives();
|
||||
closeEditorsForUndoCheckOut(archives);
|
||||
|
||||
List<Archive> archiveList = archiveProvider.getArchives();
|
||||
List<DomainFileArchive> unmodifiedCheckOutsList = new ArrayList<>();
|
||||
List<DomainFileArchive> modifiedCheckOutsList = new ArrayList<>();
|
||||
for (Archive archive2 : archiveList) {
|
||||
ProjectArchive archive = (ProjectArchive) archive2;
|
||||
List<ProjectDataTypeArchive> unmodifiedCheckOutsList = new ArrayList<>();
|
||||
List<ProjectDataTypeArchive> modifiedCheckOutsList = new ArrayList<>();
|
||||
for (ProjectDataTypeArchive archive : archives) {
|
||||
DomainFile domainFile = archive.getDomainFile();
|
||||
if (domainFile.isCheckedOut()) {
|
||||
if (domainFile.modifiedSinceCheckout() || domainFile.isChanged()) {
|
||||
@@ -119,10 +123,9 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
}
|
||||
}
|
||||
|
||||
private void closeEditorsForUndoCheckOut() {
|
||||
private void closeEditorsForUndoCheckOut(List<ProjectDataTypeArchive> archives) {
|
||||
DataTypeEditorManager editorManager = dtmPlugin.getEditorManager();
|
||||
List<Archive> archiveList = archiveProvider.getArchives();
|
||||
for (Archive archive : archiveList) {
|
||||
for (ProjectDataTypeArchive archive : archives) {
|
||||
if (!editorManager.checkEditors(archive.getDataTypeManager(), true)) {
|
||||
continue;
|
||||
}
|
||||
@@ -140,12 +143,12 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
* @param modifiedArchivesList the list of archives that have been modified
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
protected void undoCheckOuts(List<DomainFileArchive> unmodifiedArchivesList,
|
||||
List<DomainFileArchive> modifiedArchivesList) throws CancelledException {
|
||||
protected void undoCheckOuts(List<ProjectDataTypeArchive> unmodifiedArchivesList,
|
||||
List<ProjectDataTypeArchive> modifiedArchivesList) throws CancelledException {
|
||||
boolean saveCopy = false;
|
||||
DomainFile[] selectedFiles = new DomainFile[0];
|
||||
boolean undoWasCancelled = false;
|
||||
List<DomainFileArchive> selectedArchives = modifiedArchivesList;
|
||||
List<ProjectDataTypeArchive> selectedArchives = modifiedArchivesList;
|
||||
|
||||
// Now confirm the modified ones and undo checkout for the ones the user indicates.
|
||||
if (modifiedArchivesList.size() > 0) {
|
||||
UndoActionDialog dialog = new UndoActionDialog("Confirm Undo Checkout",
|
||||
@@ -153,14 +156,14 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
int actionID = dialog.showDialog(tool);
|
||||
if (actionID != UndoActionDialog.CANCEL) {
|
||||
saveCopy = dialog.saveCopy();
|
||||
selectedFiles = dialog.getSelectedDomainFiles();
|
||||
DomainFile[] selectedFiles = dialog.getSelectedDomainFiles();
|
||||
selectedArchives = getMatchingArchives(modifiedArchivesList, selectedFiles);
|
||||
}
|
||||
else {
|
||||
throw new CancelledException();
|
||||
}
|
||||
}
|
||||
if ((unmodifiedArchivesList.size() > 0) || (selectedFiles.length > 0)) {
|
||||
if ((unmodifiedArchivesList.size() > 0) || (selectedArchives.size() > 0)) {
|
||||
tool.execute(new DataTypeArchiveUndoCheckOutTask(unmodifiedArchivesList,
|
||||
selectedArchives, saveCopy));
|
||||
}
|
||||
@@ -169,12 +172,12 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
}
|
||||
}
|
||||
|
||||
private List<DomainFileArchive> getMatchingArchives(List<DomainFileArchive> archivesList,
|
||||
DomainFile[] selectedFiles) {
|
||||
List<DomainFileArchive> archiveList =
|
||||
new ArrayList<>(selectedFiles.length);
|
||||
private List<ProjectDataTypeArchive> getMatchingArchives(
|
||||
List<ProjectDataTypeArchive> archivesList, DomainFile[] selectedFiles) {
|
||||
|
||||
List<ProjectDataTypeArchive> archiveList = new ArrayList<>(selectedFiles.length);
|
||||
for (DomainFile domainFile : selectedFiles) {
|
||||
DomainFileArchive archive = getArchiveForDomainFile(archivesList, domainFile);
|
||||
ProjectDataTypeArchive archive = getArchiveForDomainFile(archivesList, domainFile);
|
||||
if (archive != null) {
|
||||
archiveList.add(archive);
|
||||
}
|
||||
@@ -187,9 +190,9 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
return archiveList;
|
||||
}
|
||||
|
||||
private DomainFileArchive getArchiveForDomainFile(List<DomainFileArchive> archivesList,
|
||||
DomainFile domainFile) {
|
||||
for (DomainFileArchive domainFileArchive : archivesList) {
|
||||
private ProjectDataTypeArchive getArchiveForDomainFile(
|
||||
List<ProjectDataTypeArchive> archivesList, DomainFile domainFile) {
|
||||
for (ProjectDataTypeArchive domainFileArchive : archivesList) {
|
||||
if (domainFileArchive.getDomainFile() == domainFile) {
|
||||
return domainFileArchive;
|
||||
}
|
||||
@@ -197,10 +200,10 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<DomainFile> getDomainFileList(List<DomainFileArchive> modifiedArchivesList) {
|
||||
private List<DomainFile> getDomainFileList(List<ProjectDataTypeArchive> modifiedArchivesList) {
|
||||
List<DomainFile> dfList = new ArrayList<>(modifiedArchivesList.size());
|
||||
for (DomainFileArchive dfArchive : modifiedArchivesList) {
|
||||
dfList.add(dfArchive.getDomainFile());
|
||||
for (ProjectDataTypeArchive archive : modifiedArchivesList) {
|
||||
dfList.add(archive.getDomainFile());
|
||||
}
|
||||
return dfList;
|
||||
}
|
||||
@@ -224,8 +227,8 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
* Task for undoing check out of files that are in version control.
|
||||
*/
|
||||
private class DataTypeArchiveUndoCheckOutTask extends Task {
|
||||
private List<DomainFileArchive> unmodifiedCheckOutsList;
|
||||
private List<DomainFileArchive> modifiedCheckedOutFiles;
|
||||
private List<ProjectDataTypeArchive> unmodifiedCheckOutsList;
|
||||
private List<ProjectDataTypeArchive> modifiedCheckedOutFiles;
|
||||
private boolean saveCopy;
|
||||
|
||||
/**
|
||||
@@ -235,8 +238,8 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
* @param saveCopy true indicates that copies of the modified files should be made
|
||||
* before undo of the checkout.
|
||||
*/
|
||||
DataTypeArchiveUndoCheckOutTask(List<DomainFileArchive> unmodifiedCheckOutsList,
|
||||
List<DomainFileArchive> modifiedCheckedOutFiles, boolean saveCopy) {
|
||||
DataTypeArchiveUndoCheckOutTask(List<ProjectDataTypeArchive> unmodifiedCheckOutsList,
|
||||
List<ProjectDataTypeArchive> modifiedCheckedOutFiles, boolean saveCopy) {
|
||||
super("Undo Check Out", true, true, true);
|
||||
|
||||
this.unmodifiedCheckOutsList = unmodifiedCheckOutsList;
|
||||
@@ -246,21 +249,20 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
DataTypeArchiveGTree gTree = dtmPlugin.getProvider().getGTree();
|
||||
GTreeState treeState = gTree.getTreeState();
|
||||
try {
|
||||
for (DomainFileArchive archive : unmodifiedCheckOutsList) {
|
||||
ArchiveManager archiveManager = dtmPlugin.getArchiveManager();
|
||||
for (ProjectDataTypeArchive archive : unmodifiedCheckOutsList) {
|
||||
DomainFile df = archive.getDomainFile();
|
||||
if (df.isCheckedOut() && (dtmPlugin != null)) {
|
||||
// TODO Need to close archive here if it is open.
|
||||
archive.close();
|
||||
|
||||
archiveManager.closeArchive(archive);
|
||||
df.undoCheckout(false);
|
||||
|
||||
// TODO Need to open the archive here if it got closed above.
|
||||
dtmPlugin.openArchive(df);
|
||||
|
||||
archiveManager.openProjectArchiveInTask(df, DomainFile.DEFAULT_VERSION,
|
||||
Upgrade.ASK, Recover.ASK, false);
|
||||
}
|
||||
}
|
||||
for (DomainFileArchive currentArchive : modifiedCheckedOutFiles) {
|
||||
for (ProjectDataTypeArchive currentArchive : modifiedCheckedOutFiles) {
|
||||
monitor.checkCancelled();
|
||||
DomainFile currentDF = currentArchive.getDomainFile();
|
||||
|
||||
@@ -271,14 +273,10 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
|
||||
monitor.setMessage("Undoing Check Out " + currentDF.getName());
|
||||
|
||||
// TODO Need to close archive here if it is open.
|
||||
currentArchive.close();
|
||||
|
||||
archiveManager.closeArchive(currentArchive);
|
||||
currentDF.undoCheckout(saveCopy);
|
||||
|
||||
// TODO Need to open the archive here if it got closed above.
|
||||
dtmPlugin.openArchive(currentDF);
|
||||
|
||||
archiveManager.openProjectArchiveInTask(currentDF, DomainFile.DEFAULT_VERSION,
|
||||
Upgrade.ASK, Recover.ASK, true);
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
@@ -287,6 +285,7 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont
|
||||
catch (IOException e) {
|
||||
ClientUtil.handleException(repository, e, "Undo Check Out", tool.getToolFrame());
|
||||
}
|
||||
gTree.restoreTreeState(treeState);
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.DataTreeDialogType;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class CreateProjectArchiveDialog extends DataTreeDialog {
|
||||
|
||||
private Object consumer;
|
||||
private ProjectDataTypeArchive archive;
|
||||
|
||||
public CreateProjectArchiveDialog(Object consumer) {
|
||||
super(null, "Create Project Archive", DataTreeDialogType.CREATE);
|
||||
this.consumer = consumer;
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DATA_MANAGER, "New_Project_Data_Type_Archive"));
|
||||
}
|
||||
|
||||
public ProjectDataTypeArchive getArchive() {
|
||||
return archive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
DomainFolder folder = getDomainFolder();
|
||||
String name = getNameText();
|
||||
if (name.length() == 0) {
|
||||
setStatusText("Please enter a name");
|
||||
return;
|
||||
}
|
||||
if (folder == null) {
|
||||
setStatusText("Please select a folder");
|
||||
return;
|
||||
}
|
||||
DomainFile file = folder.getFile(name);
|
||||
if (file != null) {
|
||||
setStatusText("Choose a name that doesn't exist");
|
||||
return;
|
||||
}
|
||||
if (createArchive()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createArchive() {
|
||||
DomainFolder domainFolder = getDomainFolder();
|
||||
String archiveName = getNameText();
|
||||
try {
|
||||
archive =
|
||||
DataTypeArchiveFactory.createProjectArchive(domainFolder, archiveName, consumer);
|
||||
return true;
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
setStatusText("Duplicate Name: " + e.getMessage());
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
setStatusText("Invalid Name: " + e.getMessage());
|
||||
}
|
||||
catch (IOException e) {
|
||||
setStatusText("Unexpected IOException!");
|
||||
Msg.showError(null, getComponent(), "Unexpected Exception", e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.ConnectException;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.framework.client.ClientUtil;
|
||||
import ghidra.framework.client.NotConnectedException;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.TransactionInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DataTypeArchiveSaveTask extends Task {
|
||||
private static final String CONTENT_NAME = "Data Type Archive";
|
||||
private PersistentDataTypeArchive archive;
|
||||
private PluginTool tool;
|
||||
|
||||
DataTypeArchiveSaveTask(PersistentDataTypeArchive archive, PluginTool tool) {
|
||||
super("Save " + archive.getName(), true, true, true);
|
||||
this.archive = archive;
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
monitor.setMessage("Saving " + archive.getName() + "...");
|
||||
if (acquireSaveLock(archive)) {
|
||||
try {
|
||||
archive.save(null, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// O.K., expected
|
||||
}
|
||||
catch (NotConnectedException e) {
|
||||
ClientUtil.promptForReconnect(tool.getProject().getRepository(),
|
||||
tool.getToolFrame());
|
||||
}
|
||||
catch (ConnectException e) {
|
||||
ClientUtil.promptForReconnect(tool.getProject().getRepository(),
|
||||
tool.getToolFrame());
|
||||
}
|
||||
catch (IOException e) {
|
||||
ClientUtil.handleException(tool.getProject().getRepository(), e, "Save File",
|
||||
tool.getToolFrame());
|
||||
}
|
||||
finally {
|
||||
archive.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean acquireSaveLock(DomainObject domainObject) {
|
||||
if (!domainObject.lock(null)) {
|
||||
String title = "Save " + CONTENT_NAME + " (Busy)";
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("The " + CONTENT_NAME + " is currently being modified by \n");
|
||||
buf.append("the following actions:\n ");
|
||||
TransactionInfo t = domainObject.getCurrentTransactionInfo();
|
||||
List<String> list = t.getOpenSubTransactions();
|
||||
for (String element : list) {
|
||||
buf.append("\n ");
|
||||
buf.append(element);
|
||||
}
|
||||
buf.append("\n \n");
|
||||
buf.append(
|
||||
"WARNING! The above task(s) should be cancelled before attempting a Save.\n");
|
||||
buf.append("Only proceed if unable to cancel them.\n \n");
|
||||
buf.append(
|
||||
"If you continue, all changes made by these tasks, as well as any other overlapping task,\n");
|
||||
buf.append(
|
||||
"will be LOST and subsequent transaction errors may occur while these tasks remain active.\n \n");
|
||||
|
||||
int result = OptionDialog.showOptionDialog(tool.getToolFrame(), title, buf.toString(),
|
||||
"Save Archive!", OptionDialog.WARNING_MESSAGE);
|
||||
|
||||
if (result == OptionDialog.OPTION_ONE) {
|
||||
domainObject.forceLock(true, "Save Archive");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.archive;
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
+476
-160
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -16,18 +15,19 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
/**
|
||||
* Manages the attributes for data types; used by the manage data types
|
||||
* dialog to populate the data types tree structure.
|
||||
@@ -110,8 +110,8 @@ public class DataTypePropertyManager {
|
||||
programDataTypesManager = null;
|
||||
}
|
||||
|
||||
void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
DataTypeManager dataTypeManager = domainObject.getDataTypeManager();
|
||||
void domainObjectRestored(PersistentDataTypeArchive archive) {
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
if (dataTypeManager != programDataTypesManager) {
|
||||
return; // Ignore since not our program data type manager.
|
||||
}
|
||||
|
||||
+14
-15
@@ -25,7 +25,6 @@ import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.lang3.Strings;
|
||||
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.app.util.html.HTMLDataTypeRepresentation;
|
||||
import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation;
|
||||
@@ -48,17 +47,17 @@ public class DataTypeSynchronizer {
|
||||
/**
|
||||
* Creates a DataTypeSynchronizer to be used for synchronizing data types between a program
|
||||
* and an archive.
|
||||
* @param dataTypeManagerHandler the handler that manages all the open data type managers
|
||||
* @param archiveManager the handler that manages all the open data type managers
|
||||
* whether built-in, program, project data type archive or file data type archive.
|
||||
* @param dataTypeManager the program data type manager.
|
||||
* @param source the data type source archive information indicating the associated archive for
|
||||
* synchronizing.
|
||||
*/
|
||||
public DataTypeSynchronizer(DataTypeManagerHandler dataTypeManagerHandler,
|
||||
DataTypeManager dataTypeManager, SourceArchive source) {
|
||||
public DataTypeSynchronizer(ArchiveManager archiveManager, DataTypeManager dataTypeManager,
|
||||
SourceArchive source) {
|
||||
this.dataTypeManager = dataTypeManager;
|
||||
this.sourceArchive = source;
|
||||
this.sourceDTM = dataTypeManagerHandler.getDataTypeManager(source);
|
||||
this.sourceDTM = archiveManager.getDataTypeManager(source);
|
||||
}
|
||||
|
||||
public List<DataTypeSyncInfo> findOutOfSynchDataTypes() {
|
||||
@@ -150,13 +149,13 @@ public class DataTypeSynchronizer {
|
||||
/**
|
||||
* Commits a single program data type's changes to the associated source data type in the
|
||||
* archive.
|
||||
* @param dtmHandler the handler that manages data types
|
||||
* @param archiveManager the archive manager
|
||||
* @param dt the program data type
|
||||
* @return true if the commit succeeds
|
||||
*/
|
||||
public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||
public static boolean commit(ArchiveManager archiveManager, DataType dt) {
|
||||
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
||||
DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive);
|
||||
if (sourceDTM == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -167,14 +166,14 @@ public class DataTypeSynchronizer {
|
||||
/**
|
||||
* Updates a single data type in the program to match the associated source data type from the
|
||||
* archive.
|
||||
* @param dtmHandler the handler that manages data types
|
||||
* @param archiveManager the handler that manages data types
|
||||
* @param dt the data type
|
||||
* @return true if the update succeeds
|
||||
*/
|
||||
public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||
public static boolean update(ArchiveManager archiveManager, DataType dt) {
|
||||
DataTypeManager dataTypeManager = dt.getDataTypeManager();
|
||||
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||
DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive);
|
||||
DataTypeManager sourceDtm = archiveManager.getDataTypeManager(sourceArchive);
|
||||
if (dataTypeManager == null || sourceDtm == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -275,7 +274,7 @@ public class DataTypeSynchronizer {
|
||||
|
||||
}
|
||||
|
||||
public static DataTypeSyncState getSyncStatus(DataTypeManagerHandler handler,
|
||||
public static DataTypeSyncState getSyncStatus(ArchiveManager archiveManager,
|
||||
DataType dataType) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
@@ -287,7 +286,7 @@ public class DataTypeSynchronizer {
|
||||
boolean hasChangedLocally =
|
||||
dataType.getLastChangeTime() != dataType.getLastChangeTimeInSourceArchive();
|
||||
|
||||
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
||||
DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive);
|
||||
DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(dataType, sourceDTM);
|
||||
if (sourceDTM == null) {
|
||||
return hasChangedLocally ? DataTypeSyncState.COMMIT : DataTypeSyncState.IN_SYNC;
|
||||
@@ -295,7 +294,7 @@ public class DataTypeSynchronizer {
|
||||
return syncInfo.getSyncState();
|
||||
}
|
||||
|
||||
public static String getDiffToolTip(DataTypeManagerHandler handler, DataType dataType) {
|
||||
public static String getDiffToolTip(ArchiveManager archiveManager, DataType dataType) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
UniversalID dataTypeID = dataType.getUniversalID();
|
||||
@@ -304,7 +303,7 @@ public class DataTypeSynchronizer {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
||||
DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive);
|
||||
boolean hasChangedLocally =
|
||||
dataType.getLastChangeTime() != dataType.getLastChangeTimeInSourceArchive();
|
||||
DataType sourceDT = null;
|
||||
|
||||
+19
-6
@@ -26,11 +26,11 @@ import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ProjectArchive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.framework.main.datatable.DomainFileContext;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext {
|
||||
@@ -90,11 +90,11 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||
domainFiles = new ArrayList<DomainFile>();
|
||||
for (TreePath path : selectionPaths) {
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
if (lastPathComponent instanceof ProjectArchiveNode) {
|
||||
ProjectArchiveNode node = (ProjectArchiveNode) lastPathComponent;
|
||||
ProjectArchive archive = (ProjectArchive) node.getArchive();
|
||||
DomainFile originalDomainFile = archive.getDomainFile();
|
||||
domainFiles.add(originalDomainFile);
|
||||
if (lastPathComponent instanceof ProjectArchiveNode node) {
|
||||
DomainFile originalDomainFile = node.getOriginalDomainFile();
|
||||
if (originalDomainFile != null) {
|
||||
domainFiles.add(originalDomainFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,4 +156,17 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||
return dataTypeNode;
|
||||
}
|
||||
|
||||
public List<ProjectDataTypeArchive> getSelectedProjectArchives() {
|
||||
List<ProjectDataTypeArchive> list = new ArrayList<>();
|
||||
|
||||
List<GTreeNode> selectedNodes = getSelectedNodes();
|
||||
for (GTreeNode node : selectedNodes) {
|
||||
if (node instanceof ProjectArchiveNode projectNode) {
|
||||
ProjectDataTypeArchive archive = projectNode.getArchive();
|
||||
list.add(archive);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+82
-126
@@ -38,19 +38,18 @@ import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.app.plugin.core.datamgr.actions.*;
|
||||
import ghidra.app.plugin.core.datamgr.actions.associate.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.app.util.datatype.DataTypeUrl;
|
||||
import ghidra.framework.main.datatree.ArchiveProvider;
|
||||
import ghidra.framework.main.datatree.VersionControlDataTypeArchiveUndoCheckoutAction;
|
||||
import ghidra.framework.main.projectdata.actions.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResolutionPolicy;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
@@ -172,12 +171,13 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
|
||||
// File group
|
||||
addLocalAction(new SaveArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new SaveArchiveAsAction(plugin));
|
||||
addLocalAction(new CloseArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new RemoveInvalidArchiveFromProgramAction(plugin)); // Archive
|
||||
|
||||
// FileEdit group
|
||||
addLocalAction(new LockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new UnlockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new OpenArchiveForEditingAction(plugin)); // Archive
|
||||
addLocalAction(new CloseArchiveForEditingAction(plugin)); // Archive
|
||||
addLocalAction(new UndoArchiveTransactionAction(plugin)); // Archive
|
||||
addLocalAction(new RedoArchiveTransactionAction(plugin)); // Archive
|
||||
|
||||
@@ -247,20 +247,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
|
||||
private void addVersionControlActions() {
|
||||
|
||||
ArchiveProvider archiveProvider = () -> {
|
||||
TreePath[] selectionPaths = archiveGTree.getSelectionPaths();
|
||||
List<Archive> selectedArchives = new ArrayList<>();
|
||||
for (TreePath path : selectionPaths) {
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
if (lastPathComponent instanceof ProjectArchiveNode) {
|
||||
ProjectArchiveNode node = (ProjectArchiveNode) lastPathComponent;
|
||||
ProjectArchive archive = (ProjectArchive) node.getArchive();
|
||||
selectedArchives.add(archive);
|
||||
}
|
||||
}
|
||||
return selectedArchives;
|
||||
};
|
||||
|
||||
VersionControlAddAction addAction = new VersionControlAddAction(plugin);
|
||||
addAction.setEnabled(false);
|
||||
|
||||
@@ -274,8 +260,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
new VersionControlCheckInAction(plugin, archiveGTree);
|
||||
checkInAction.setEnabled(false);
|
||||
|
||||
VersionControlDataTypeArchiveUndoCheckoutAction undoCheckOutAction =
|
||||
new VersionControlDataTypeArchiveUndoCheckoutAction(plugin, archiveProvider);
|
||||
ArchiveUndoCheckoutTask undoCheckOutAction =
|
||||
new ArchiveUndoCheckoutTask(plugin);
|
||||
undoCheckOutAction.setEnabled(false);
|
||||
|
||||
VersionControlShowHistoryAction showHistoryAction =
|
||||
@@ -572,35 +558,16 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private static DataType updateDataType(CategoryPath path, String dataTypeName,
|
||||
ArchiveNode archiveNode) {
|
||||
DataTypeManager dataTypeManager = archiveNode.getArchive().getDataTypeManager();
|
||||
PersistentDataTypeArchive archive) {
|
||||
if (archive == null) {
|
||||
return null;
|
||||
}
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
Category category = dataTypeManager.getCategory(path);
|
||||
return category.getDataType(dataTypeName);
|
||||
}
|
||||
|
||||
private boolean getWriteLock(DataTypeManagerPlugin dataTypePlugin, ArchiveNode archiveNode) {
|
||||
if (!isOkToLock()) {
|
||||
return false;
|
||||
}
|
||||
GTree tree = dataTypePlugin.getProvider().getGTree();
|
||||
GTreeState state = tree.getTreeState();
|
||||
if (!ArchiveUtils.lockArchive((FileArchive) archiveNode.getArchive())) {
|
||||
return false;
|
||||
}
|
||||
tree.restoreTreeState(state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean needsWriteLock(ArchiveNode archiveNode) {
|
||||
if (archiveNode instanceof FileArchiveNode) {
|
||||
FileArchiveNode fileArchiveNode = (FileArchiveNode) archiveNode;
|
||||
return !fileArchiveNode.hasWriteLock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isOkToLock() {
|
||||
private boolean askToOpenArchiveForUpdate() {
|
||||
return (OptionDialog.showYesNoDialog(archiveGTree, "Open Archive for Edit?",
|
||||
"Archive file is not modifiable.\nDo you want to open for edit?") == OptionDialog.OPTION_ONE);
|
||||
}
|
||||
@@ -623,30 +590,55 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
dataType = DataTypeUtils.getBaseDataType(dataType);
|
||||
CategoryPath path = dataType.getCategoryPath();
|
||||
String dataTypeName = dataType.getName();
|
||||
ArchiveNode archiveNode = dataTypeNode.getArchiveNode();
|
||||
DataTypeStoreNode archiveNode = dataTypeNode.getArchiveNode();
|
||||
|
||||
if (archiveNode instanceof ProjectArchiveNode && !archiveNode.isModifiable()) {
|
||||
ProjectArchiveNode projectArchive = (ProjectArchiveNode) archiveNode;
|
||||
if (projectArchive.getDomainFile().isReadOnly()) {
|
||||
Msg.showInfo(getClass(), archiveGTree, "Read-Only Archive",
|
||||
"You may not edit data type within a read-only project archive.");
|
||||
}
|
||||
else {
|
||||
Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out",
|
||||
"You must checkout this archive before you may edit data types.");
|
||||
}
|
||||
if (isUnmodifiableProjectArchive(archiveNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// must get write lock before we can edit
|
||||
if (needsWriteLock(archiveNode)) {
|
||||
if (!getWriteLock(plugin, archiveNode)) {
|
||||
return;
|
||||
if (archiveNode instanceof FileArchiveNode fileNode) {
|
||||
FileDataTypeArchive archive = fileNode.getArchive();
|
||||
if (!archive.isChangeable()) {
|
||||
if (!askToOpenArchiveForUpdate()) {
|
||||
return;
|
||||
}
|
||||
archive = openForUpdate(archive);
|
||||
dataType = updateDataType(path, dataTypeName, archive);
|
||||
}
|
||||
dataType = updateDataType(path, dataTypeName, archiveNode);
|
||||
}
|
||||
if (dataType != null) {
|
||||
plugin.getEditorManager().edit(dataType);
|
||||
}
|
||||
}
|
||||
|
||||
private FileDataTypeArchive openForUpdate(FileDataTypeArchive archive) {
|
||||
GTree tree = plugin.getProvider().getGTree();
|
||||
GTreeState state = tree.getTreeState();
|
||||
archive = plugin.getArchiveManager().reopenFileArchive(archive, true);
|
||||
tree.restoreTreeState(state);
|
||||
return archive;
|
||||
}
|
||||
|
||||
private boolean isUnmodifiableProjectArchive(DataTypeStoreNode archiveNode) {
|
||||
if (!(archiveNode instanceof ProjectArchiveNode projectNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
plugin.getEditorManager().edit(dataType);
|
||||
if (archiveNode.isModifiable()) {
|
||||
return false;
|
||||
}
|
||||
DomainFile domainFile = projectNode.getDomainFile();
|
||||
DomainFile originalDomainFile = projectNode.getOriginalDomainFile();
|
||||
if (domainFile.getVersion() == originalDomainFile.getLatestVersion() &&
|
||||
originalDomainFile.canCheckout()) {
|
||||
Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out",
|
||||
"You must checkout this archive before you may edit data types.");
|
||||
}
|
||||
else {
|
||||
Msg.showInfo(getClass(), archiveGTree, "Archive Opened Read-Only",
|
||||
"You may not edit data type within a read-only project archive.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void restore(SaveState saveState) {
|
||||
@@ -692,55 +684,26 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
return archiveGTree;
|
||||
}
|
||||
|
||||
void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
void domainObjectRestored(PersistentDataTypeArchive archive) {
|
||||
if (archiveGTree == null) {
|
||||
return; // nothing to update
|
||||
}
|
||||
if (domainObject instanceof Program) {
|
||||
Program program = (Program) domainObject;
|
||||
Program programInTree = plugin.getProgram(); // may be null
|
||||
if (program == programInTree) {
|
||||
DataTypeArchiveGTree gTree = getGTree();
|
||||
ArchiveNode node = getProgramArchiveNode();
|
||||
if (node != null) {
|
||||
GTreeState state = gTree.getTreeState(node);
|
||||
node.structureChanged();
|
||||
gTree.restoreTreeState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
DataTypeArchive dataTypeArchive = (DataTypeArchive) domainObject;
|
||||
DataTypeArchiveGTree gTree = getGTree();
|
||||
ArchiveNode node = getDataTypeArchiveNode(dataTypeArchive);
|
||||
if (node != null) {
|
||||
GTreeState state = gTree.getTreeState(node);
|
||||
node.structureChanged();
|
||||
gTree.restoreTreeState(state);
|
||||
}
|
||||
DataTypeArchiveGTree gTree = getGTree();
|
||||
ArchiveNode node = getDataTypeArchiveNode(archive);
|
||||
if (node != null) {
|
||||
GTreeState state = gTree.getTreeState(node);
|
||||
node.structureChanged();
|
||||
gTree.restoreTreeState(state);
|
||||
}
|
||||
}
|
||||
|
||||
private ProgramArchiveNode getProgramArchiveNode() {
|
||||
private ArchiveNode getDataTypeArchiveNode(PersistentDataTypeArchive dataTypeArchive) {
|
||||
GTreeNode rootNode = getGTree().getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
if (node instanceof ProgramArchiveNode programNode) {
|
||||
return programNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) {
|
||||
GTreeNode rootNode = getGTree().getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
Archive archive = archiveNode.getArchive();
|
||||
if (archive instanceof ProjectArchive) {
|
||||
ProjectArchive projectArchive = (ProjectArchive) archive;
|
||||
if (projectArchive.getDataTypeManager() == dataTypeArchive.getDataTypeManager()) {
|
||||
if (node instanceof ArchiveNode archiveNode) {
|
||||
PersistentDataTypeArchive archive = archiveNode.getArchive();
|
||||
if (archive == dataTypeArchive) {
|
||||
return archiveNode;
|
||||
}
|
||||
}
|
||||
@@ -774,7 +737,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
|
||||
Category category = dataTypeManager.getCategory(dataType.getCategoryPath());
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot();
|
||||
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||
DataTypeStoreNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||
if (archiveNode == null) {
|
||||
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
|
||||
"be filtered out of view or may have been closed (Data Type Manager)");
|
||||
@@ -824,7 +787,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot();
|
||||
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||
DataTypeStoreNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||
if (archiveNode == null) {
|
||||
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
|
||||
"be filtered out of view or may have been closed (Data Type Manager)");
|
||||
@@ -924,8 +887,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private void restoreProgramTreeState(ProgramArchiveNode programNode) {
|
||||
ProgramArchive programArchive = (ProgramArchive) programNode.getArchive();
|
||||
Program program = programArchive.getProgram();
|
||||
Program program = programNode.getProgram();
|
||||
long id = program.getUniqueProgramID();
|
||||
TreePath selectedPath = programTreeState.get(id);
|
||||
if (selectedPath == null) {
|
||||
@@ -946,8 +908,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
// of the program node, so only save the path if there is a single selection. This will
|
||||
// be helpful in the case that the user was working with a single data type in the program.
|
||||
//
|
||||
ProgramArchive programArchive = (ProgramArchive) programNode.getArchive();
|
||||
Program program = programArchive.getProgram();
|
||||
Program program = programNode.getProgram();
|
||||
long id = program.getUniqueProgramID();
|
||||
GTreeState state = archiveGTree.getTreeState();
|
||||
List<TreePath> paths = state.getSelectedPaths();
|
||||
@@ -987,16 +948,11 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
clearDataTypePreview();
|
||||
}
|
||||
|
||||
void archiveClosed(DataTypeManager dtm) {
|
||||
dataTypeManagerChanged(dtm);
|
||||
void archiveClosed(PersistentDataTypeArchive archive) {
|
||||
archiveChanged(archive.getName());
|
||||
}
|
||||
|
||||
void archiveChanged(Archive archive) {
|
||||
DataTypeManager dtm = archive.getDataTypeManager();
|
||||
dataTypeManagerChanged(dtm);
|
||||
}
|
||||
|
||||
private void dataTypeManagerChanged(DataTypeManager dtm) {
|
||||
void archiveChanged(String name) {
|
||||
|
||||
if (lastPreviewNode == null || !(lastPreviewNode instanceof DataTypeNode)) {
|
||||
return;
|
||||
@@ -1005,9 +961,10 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
DataTypeNode dtNode = (DataTypeNode) lastPreviewNode;
|
||||
DataType dt = dtNode.getDataType();
|
||||
DataTypeManager dtManager = dt.getDataTypeManager();
|
||||
|
||||
// note: compare using name; an equality check will fail if the manager is reloaded
|
||||
if (dtm.getName().equals(dtManager.getName())) {
|
||||
// notes: the archive name and the datatypeManager name are the same. If the archive
|
||||
// changed because it was closed, then its datatype manager will be null. So just
|
||||
// compare the datatype's manager's name with the archive's name.
|
||||
if (name.equals(dtManager.getName())) {
|
||||
lastPreviewNode = null;
|
||||
}
|
||||
}
|
||||
@@ -1016,9 +973,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot();
|
||||
List<GTreeNode> allChildren = rootNode.getChildren();
|
||||
for (GTreeNode node : allChildren) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.getArchive() instanceof ProgramArchive) {
|
||||
archiveNode.nodeChanged();
|
||||
if (node instanceof ProgramArchiveNode programNode) {
|
||||
programNode.nodeChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1087,14 +1043,14 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
private class ProgramNodeUpdateListener implements ArchiveRootNodeListener {
|
||||
|
||||
@Override
|
||||
public void archiveNodeAdded(ArchiveNode node) {
|
||||
public void archiveNodeAdded(DataTypeStoreNode node) {
|
||||
if (node instanceof ProgramArchiveNode programNode) {
|
||||
restoreProgramTreeState(programNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void archiveNodeRemoved(ArchiveNode node) {
|
||||
public void archiveNodeRemoved(DataTypeStoreNode node) {
|
||||
if (node instanceof ProgramArchiveNode programNode) {
|
||||
saveProgramTreeState(programNode);
|
||||
}
|
||||
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||
import ghidra.framework.client.ClientUtil;
|
||||
import ghidra.framework.client.RepositoryAdapter;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class OpenDomainFileTask extends Task {
|
||||
private DomainFile domainFile;
|
||||
private int version;
|
||||
private DataTypeManagerPlugin dtmPlugin;
|
||||
private DataTypeManagerHandler dtmHandler;
|
||||
private PluginTool tool;
|
||||
private DataTypeArchive dtArchive = null;
|
||||
|
||||
OpenDomainFileTask(DomainFile domainFile, int version, PluginTool tool,
|
||||
DataTypeManagerPlugin dtmPlugin) {
|
||||
|
||||
super("Open Project Data Type Archive", true, true, true);
|
||||
this.domainFile = domainFile;
|
||||
this.dtmPlugin = dtmPlugin;
|
||||
this.dtmHandler = dtmPlugin.getDataTypeManagerHandler();
|
||||
this.tool = tool;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
DataTypeArchive getArchive() {
|
||||
return dtArchive;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
|
||||
if (isFileOpen()) {
|
||||
return;
|
||||
}
|
||||
boolean associateWithOriginalDomainFile = true;
|
||||
if (version != DomainFile.DEFAULT_VERSION) {
|
||||
openReadOnlyFile(monitor);
|
||||
associateWithOriginalDomainFile = false;
|
||||
}
|
||||
else if (domainFile.isReadOnly()) {
|
||||
openReadOnlyFile(monitor);
|
||||
}
|
||||
else if (domainFile.isVersioned() && !domainFile.isCheckedOut()) {
|
||||
openReadOnlyFile(monitor);
|
||||
}
|
||||
else {
|
||||
openUnversionedFile(monitor);
|
||||
}
|
||||
if (dtArchive != null) {
|
||||
openFileInTree(associateWithOriginalDomainFile);
|
||||
dtArchive.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFileOpen() {
|
||||
List<Archive> dtArchiveList = dtmHandler.getAllArchives();
|
||||
for (Archive archive : dtArchiveList) {
|
||||
if (archive instanceof ProjectArchive) {
|
||||
ProjectArchive projectArchive = (ProjectArchive) archive;
|
||||
DomainFile archiveDomainFile = projectArchive.getDomainFile();
|
||||
if (filesMatch(domainFile, archiveDomainFile)) {
|
||||
// archive = projectArchive;
|
||||
// dtmHandler.open // TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean filesMatch(DomainFile file1, DomainFile file2) {
|
||||
if (!file1.getPathname().equals(file2.getPathname())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file1.isCheckedOut() != file2.isCheckedOut()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SystemUtilities.isEqual(file1.getProjectLocator(), file2.getProjectLocator())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int otherVersion = file2.isReadOnly() ? file2.getVersion() : -1;
|
||||
return version == otherVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open archive in an immutable fashion. Unlike ProgramDB, we do not want to
|
||||
* allow upgrade or modification of a read-only archve (e.g., not-checked-out).
|
||||
* @param monitor task monitor
|
||||
*/
|
||||
private void openReadOnlyFile(TaskMonitor monitor) {
|
||||
String fileDescr =
|
||||
((version != DomainFile.DEFAULT_VERSION) ? "version " + version + " of " : "") +
|
||||
domainFile.getName();
|
||||
String contentType = null;
|
||||
try {
|
||||
monitor.setMessage("Opening " + fileDescr);
|
||||
contentType = domainFile.getContentType();
|
||||
dtArchive =
|
||||
(DataTypeArchive) domainFile.getImmutableDomainObject(this, version, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// we don't care, the task has been canceled
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (domainFile.isVersioned() && domainFile.isInWritableProject()) {
|
||||
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
|
||||
"Get Versioned Object", null);
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, null, "Project Archive Open Error",
|
||||
"Error occurred while opening " + fileDescr, e);
|
||||
}
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(tool.getToolFrame(), domainFile.getName(),
|
||||
contentType, "Open", false, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void openUnversionedFile(TaskMonitor monitor) {
|
||||
monitor.setMessage("Opening " + domainFile.getName());
|
||||
String contentType = null;
|
||||
try {
|
||||
final boolean recoverFile = isRecoveryOK(domainFile);
|
||||
contentType = domainFile.getContentType();
|
||||
try {
|
||||
dtArchive =
|
||||
(DataTypeArchive) domainFile.getDomainObject(this, false, recoverFile, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (VersionExceptionHandler.isUpgradeOK(null, domainFile, "Open", e)) {
|
||||
dtArchive = (DataTypeArchive) domainFile.getDomainObject(this, true,
|
||||
recoverFile, monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType,
|
||||
"Open", false, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// we don't care, the task has been canceled
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (domainFile.isInWritableProject() && (e instanceof IOException)) {
|
||||
RepositoryAdapter repo = domainFile.getParent().getProjectData().getRepository();
|
||||
ClientUtil.handleException(repo, e, "Open File", null);
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, null, "Error Opening " + domainFile.getName(),
|
||||
"Opening data type archive failed.\n" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRecoveryOK(final DomainFile dfile)
|
||||
throws InterruptedException, InvocationTargetException {
|
||||
final boolean[] recoverFile = new boolean[] { false };
|
||||
if (dfile.isInWritableProject() && dfile.canRecover()) {
|
||||
Runnable r = () -> {
|
||||
int option = OptionDialog.showYesNoDialog(null, "Crash Recovery Data Found",
|
||||
"<html>" + HTMLUtilities.escapeHTML(dfile.getName()) + " has crash data.<br>" +
|
||||
"Would you like to recover unsaved changes?");
|
||||
recoverFile[0] = (option == OptionDialog.OPTION_ONE);
|
||||
};
|
||||
SwingUtilities.invokeAndWait(r);
|
||||
}
|
||||
return recoverFile[0];
|
||||
}
|
||||
|
||||
private void openFileInTree(boolean associatedWithOriginalDomainFile) {
|
||||
DataTypesProvider provider = dtmPlugin.getProvider();
|
||||
GTree tree = provider.getGTree();
|
||||
DataTypeManagerHandler manager = dtmPlugin.getDataTypeManagerHandler();
|
||||
DomainFile df = associatedWithOriginalDomainFile ? domainFile : dtArchive.getDomainFile();
|
||||
Archive archive = manager.openArchive(dtArchive, df);
|
||||
GTreeNode node = getNodeForArchive(tree, archive);
|
||||
if (node != null) {
|
||||
tree.setSelectedNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
for (GTreeNode node : rootNode.getChildren()) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.getArchive() == archive) {
|
||||
return archiveNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.services.Upgrade;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.VersionExceptionHandler;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class OpenFileArchiveTask extends Task {
|
||||
private ResourceFile file;
|
||||
private FileDataTypeArchive archive = null;
|
||||
private Object consumer;
|
||||
private Upgrade upgradeStrategy;
|
||||
private boolean openForUpdate;
|
||||
|
||||
OpenFileArchiveTask(ResourceFile file, boolean openForUpdate,
|
||||
Upgrade upgradeStrategy, Object consumer) {
|
||||
super("Opening File DataType Archive " + file.getName(), false, false, true);
|
||||
this.file = file;
|
||||
this.openForUpdate = openForUpdate;
|
||||
this.upgradeStrategy = upgradeStrategy;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
FileDataTypeArchive getArchive() {
|
||||
return archive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
try {
|
||||
archive = openArchive(monitor);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Msg.showError(this, null, "Open File Archive Failed",
|
||||
file.getAbsolutePath() + " not found!");
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Open File Archive Failed",
|
||||
e.getMessage() + ": " + file.getName());
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, file.getName(), "Archive", "open", false,
|
||||
e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// user cancelled, nothing to report
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public FileDataTypeArchive openArchive(TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
|
||||
if (!openForUpdate) {
|
||||
return DataTypeArchiveFactory.openReadOnly(file, consumer, monitor);
|
||||
}
|
||||
try {
|
||||
return DataTypeArchiveFactory.openForUpdate(file, false, consumer, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (shouldUpgrade(e)) {
|
||||
return DataTypeArchiveFactory.openForUpdate(file, true, consumer, monitor);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpgrade(VersionException e) {
|
||||
if (!e.isUpgradable()) {
|
||||
return false;
|
||||
}
|
||||
switch (upgradeStrategy) {
|
||||
case YES:
|
||||
return true;
|
||||
case NO:
|
||||
return false;
|
||||
case ASK:
|
||||
default:
|
||||
return askToUpgrade();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean askToUpgrade() {
|
||||
return OptionDialog.showOptionDialog(null,
|
||||
"Upgrade File Archive: " + file.getName(),
|
||||
"File archive is an older version.\n" +
|
||||
"Do you want to upgrade it to the latest version?",
|
||||
"Upgrade",
|
||||
OptionDialog.QUESTION_MESSAGE) == OptionDialog.YES_OPTION;
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.services.Recover;
|
||||
import ghidra.app.services.Upgrade;
|
||||
import ghidra.framework.client.ClientUtil;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.database.dtarchive.DataTypeArchiveFactory;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class OpenProjectArchiveTask extends Task {
|
||||
private DomainFile file;
|
||||
private int version;
|
||||
private ProjectDataTypeArchive archive = null;
|
||||
private Object consumer;
|
||||
private Recover recoverStrategy;
|
||||
private Upgrade upgradeStrategy;
|
||||
|
||||
OpenProjectArchiveTask(DomainFile file, int version, Upgrade upgradeStrategy,
|
||||
Recover recoverStrategy, Object consumer) {
|
||||
super("Open Project Data Type Archive", true, true, true);
|
||||
this.file = file;
|
||||
this.upgradeStrategy = upgradeStrategy;
|
||||
this.recoverStrategy = recoverStrategy;
|
||||
this.consumer = consumer;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
ProjectDataTypeArchive getArchive() {
|
||||
return archive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
monitor.setMessage(getDescription());
|
||||
try {
|
||||
archive = openArchive(monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
String contentType = file.getContentType();
|
||||
VersionExceptionHandler.showVersionError(null, file.getName(),
|
||||
contentType, "Open", false, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// do nothing, user cancelled
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (file.isVersioned() && file.isInWritableProject()) {
|
||||
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
|
||||
"Get Versioned Object", null);
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, null, "Project Archive Open Error",
|
||||
"Error occurred while opening " + file.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getDescription() {
|
||||
String description = "Opening " + file.getName();
|
||||
if (version != DomainFile.DEFAULT_VERSION) {
|
||||
description += " (version " + version + ")";
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
public ProjectDataTypeArchive openArchive(TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
|
||||
if (shouldOpenImmutable(file)) {
|
||||
return DataTypeArchiveFactory.openReadOnly(file, version, consumer, monitor);
|
||||
}
|
||||
boolean okToRecover = shouldRecover();
|
||||
try {
|
||||
return DataTypeArchiveFactory.openForUpdate(file, consumer, false, okToRecover,
|
||||
monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (shouldUpgrade(e)) {
|
||||
return DataTypeArchiveFactory.openForUpdate(file, consumer, true, okToRecover,
|
||||
monitor);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpgrade(VersionException e) {
|
||||
if (!e.isUpgradable()) {
|
||||
return false;
|
||||
}
|
||||
switch (upgradeStrategy) {
|
||||
case YES:
|
||||
return true;
|
||||
case NO:
|
||||
return false;
|
||||
case ASK:
|
||||
default:
|
||||
return VersionExceptionHandler.isUpgradeOK(null, file, "Open File Archive", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldOpenImmutable(DomainFile domainFile) {
|
||||
if (version != DomainFile.DEFAULT_VERSION) {
|
||||
return true;
|
||||
}
|
||||
if (domainFile.isReadOnly()) {
|
||||
return true;
|
||||
}
|
||||
return domainFile.isVersioned() && !domainFile.isCheckedOut();
|
||||
}
|
||||
|
||||
private boolean shouldRecover() {
|
||||
if (!file.isInWritableProject()) {
|
||||
return false;
|
||||
}
|
||||
switch (recoverStrategy) {
|
||||
case YES:
|
||||
return true;
|
||||
case NO:
|
||||
return false;
|
||||
case ASK:
|
||||
default:
|
||||
return askToRecover();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean askToRecover() {
|
||||
if (!file.canRecover()) {
|
||||
return false;
|
||||
}
|
||||
int option = OptionDialog.showYesNoDialog(null, "Crash Recovery Data Found",
|
||||
"<html>" + HTMLUtilities.escapeHTML(file.getName()) + " has crash data.<br>" +
|
||||
"Would you like to recover unsaved changes?");
|
||||
return (option == OptionDialog.OPTION_ONE);
|
||||
}
|
||||
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.DataTreeDialogType;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
public class ProjectArchiveSaveAsDialog extends DataTreeDialog {
|
||||
private static final String CONTENT_NAME = "Data Type Archive";
|
||||
|
||||
private ProjectDataTypeArchive archive;
|
||||
|
||||
private PluginTool tool;
|
||||
|
||||
public ProjectArchiveSaveAsDialog(PluginTool tool, ProjectDataTypeArchive archive) {
|
||||
super(null, "Save Project Archive As", DataTreeDialogType.SAVE);
|
||||
this.tool = tool;
|
||||
this.archive = archive;
|
||||
setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
DomainFolder folder = getDomainFolder();
|
||||
String name = getNameText();
|
||||
if (name.length() == 0) {
|
||||
setStatusText("Please enter a name");
|
||||
return;
|
||||
}
|
||||
if (folder == null) {
|
||||
setStatusText("Please select a folder");
|
||||
return;
|
||||
}
|
||||
DomainFile file = folder.getFile(name);
|
||||
if (file != null) {
|
||||
setStatusText("Choose a name that doesn't exist");
|
||||
return;
|
||||
}
|
||||
close();
|
||||
doSaveAs();
|
||||
}
|
||||
|
||||
private void doSaveAs() {
|
||||
if (!getSaveAsLock(archive)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DomainFolder folder = getDomainFolder();
|
||||
String newName = getNameText();
|
||||
tool.prepareToSave(archive);
|
||||
TaskLauncher.launchModal("Save As", monitor -> {
|
||||
try {
|
||||
folder.createFile(newName, archive, monitor);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.showError(this, null, "Invalid Name", e.getMessage(), e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// user cancelled, nothing to do
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "I/O error ", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
archive.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getSaveAsLock(DomainObject domainObject) {
|
||||
if (!domainObject.lock(null)) {
|
||||
String title = "Save " + CONTENT_NAME + " As (Busy)";
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("The " + CONTENT_NAME +
|
||||
" is currently being modified by the following actions/tasks:\n \n");
|
||||
TransactionInfo t = domainObject.getCurrentTransactionInfo();
|
||||
List<String> list = t.getOpenSubTransactions();
|
||||
for (String element : list) {
|
||||
buf.append("\n ");
|
||||
buf.append(element);
|
||||
}
|
||||
buf.append("\n \n");
|
||||
buf.append(
|
||||
"WARNING! The above task(s) should be cancelled before attempting a Save As...\n");
|
||||
buf.append("Only proceed if unable to cancel them.\n \n");
|
||||
buf.append(
|
||||
"If you click 'Save Archive As (Rollback)' {recommended}, all changes made\n");
|
||||
buf.append("by these tasks, as well as any other overlapping task, will be LOST!\n");
|
||||
buf.append(
|
||||
"If you click 'Save As (As Is)', the archive will be saved in its current\n");
|
||||
buf.append("state which may contain some incomplete data.\n");
|
||||
buf.append("Any forced save may also result in subsequent transaction errors while\n");
|
||||
buf.append("the above tasks remain active.\n ");
|
||||
|
||||
int result = OptionDialog.showOptionDialog(null, title, buf.toString(),
|
||||
"Save Archive As (Rollback)!", "Save Archive As (As Is)!",
|
||||
OptionDialog.WARNING_MESSAGE);
|
||||
|
||||
if (result == OptionDialog.OPTION_ONE) {
|
||||
domainObject.forceLock(true, "Save Archive As");
|
||||
return true;
|
||||
}
|
||||
else if (result == OptionDialog.OPTION_TWO) {
|
||||
domainObject.forceLock(false, "Save Archive As");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+21
-22
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -27,8 +27,7 @@ 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;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
|
||||
public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAction {
|
||||
|
||||
@@ -62,28 +61,28 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
return getModifiableProjectOrFileDTM(selectionPaths) != null;
|
||||
return getModifiableProjectOrFileArchive(selectionPaths) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the corresponding undo/redo can be performed
|
||||
* @param dtm archive datatype manager
|
||||
* @param archive datatype archive
|
||||
* @return true if action can be performed on archive
|
||||
*/
|
||||
abstract protected boolean canExecute(StandAloneDataTypeManager dtm);
|
||||
abstract protected boolean canExecute(PersistentDataTypeArchive archive);
|
||||
|
||||
/**
|
||||
* Determine the next undo/redo transaction name
|
||||
* @param dtm archive datatype manager
|
||||
* @param archive datatype archive
|
||||
* @return next undo/redo transaction name
|
||||
*/
|
||||
abstract protected String getNextName(StandAloneDataTypeManager dtm);
|
||||
abstract protected String getNextName(PersistentDataTypeArchive archive);
|
||||
|
||||
/**
|
||||
* Execute the undo/redo operation on the specified archive datatype manager.
|
||||
* @param dtm archive datatype manager
|
||||
* Execute the undo/redo operation on the specified archive.
|
||||
* @param archive datatype archive
|
||||
*/
|
||||
abstract protected void execute(StandAloneDataTypeManager dtm);
|
||||
abstract protected void execute(PersistentDataTypeArchive archive);
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
@@ -92,9 +91,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
setPopupMenuData(getMenuData(getNextName(dtm)));
|
||||
PersistentDataTypeArchive dta = getModifiableProjectOrFileArchive(selectionPaths);
|
||||
if (dta != null && canExecute(dta)) {
|
||||
setPopupMenuData(getMenuData(getNextName(dta)));
|
||||
return true;
|
||||
}
|
||||
setPopupMenuData(getMenuData(null));
|
||||
@@ -109,9 +108,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
execute(dtm);
|
||||
PersistentDataTypeArchive dta = getModifiableProjectOrFileArchive(selectionPaths);
|
||||
if (dta != null && canExecute(dta)) {
|
||||
execute(dta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +121,7 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc
|
||||
return selectionPaths;
|
||||
}
|
||||
|
||||
private StandAloneDataTypeManager getModifiableProjectOrFileDTM(TreePath[] selectionPaths) {
|
||||
private PersistentDataTypeArchive getModifiableProjectOrFileArchive(TreePath[] selectionPaths) {
|
||||
// only valid if single file or project archive node is selected
|
||||
if (selectionPaths.length != 1) {
|
||||
return null;
|
||||
@@ -140,9 +139,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc
|
||||
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.isModifiable()) {
|
||||
DataTypeManager dtm = archiveNode.getArchive().getDataTypeManager();
|
||||
if (dtm instanceof StandAloneDataTypeManager archiveDtm) {
|
||||
return archiveDtm;
|
||||
PersistentDataTypeArchive archive = archiveNode.getArchive();
|
||||
if (archive instanceof PersistentDataTypeArchive dta) {
|
||||
return dta;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+6
-6
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -78,8 +78,8 @@ public class CaptureFunctionDataTypesAction extends DockingAction {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
TreePath selectionPath = gTree.getSelectionPath();
|
||||
ArchiveNode node = (ArchiveNode) selectionPath.getLastPathComponent();
|
||||
if (!node.getArchive().isModifiable()) {
|
||||
DataTypeStoreNode node = (DataTypeStoreNode) selectionPath.getLastPathComponent();
|
||||
if (!node.getDataTypeStore().isChangeable()) {
|
||||
informNotModifiable(node);
|
||||
return;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class CaptureFunctionDataTypesAction extends DockingAction {
|
||||
if (currentSelection == null || currentSelection.isEmpty()) {
|
||||
currentSelection = program.getMemory();
|
||||
}
|
||||
final DataTypeManager manager = node.getArchive().getDataTypeManager();
|
||||
final DataTypeManager manager = node.getDataTypeManager();
|
||||
final PluginTool tool = plugin.getTool();
|
||||
CaptureFunctionDataTypesCmd cmd =
|
||||
new CaptureFunctionDataTypesCmd(manager, currentSelection,
|
||||
@@ -105,7 +105,7 @@ public class CaptureFunctionDataTypesAction extends DockingAction {
|
||||
tool.executeBackgroundCommand(cmd, program);
|
||||
}
|
||||
|
||||
private void informNotModifiable(ArchiveNode node) {
|
||||
private void informNotModifiable(DataTypeStoreNode node) {
|
||||
String message;
|
||||
if (node instanceof ProgramArchiveNode) {
|
||||
message = "The program \"" + node.getName() + "\" isn't modifiable.";
|
||||
|
||||
+20
-46
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ActionContext;
|
||||
@@ -26,13 +24,11 @@ import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
import ghidra.program.model.dtarchive.ProjectDataTypeArchive;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
@@ -71,8 +67,7 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
return false;
|
||||
}
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
StandAloneDataTypeManager dtm =
|
||||
(StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager();
|
||||
DataTypeManager dtm = archiveNode.getDataTypeManager();
|
||||
|
||||
return dtm.getProgramArchitectureSummary() != null && dtm.isUpdatable();
|
||||
}
|
||||
@@ -91,7 +86,7 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
|
||||
if (node instanceof ProjectArchiveNode) {
|
||||
ProjectArchiveNode paNode = (ProjectArchiveNode) node;
|
||||
ProjectArchive pa = (ProjectArchive) paNode.getArchive();
|
||||
ProjectDataTypeArchive pa = paNode.getArchive();
|
||||
if (!pa.hasExclusiveAccess()) {
|
||||
Msg.showError(this, null, "Clear Program Architecture Failed",
|
||||
"Clearing program-architecture on Project Archive requires exclusive checkout.");
|
||||
@@ -100,10 +95,9 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
}
|
||||
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
StandAloneDataTypeManager dtm =
|
||||
(StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager();
|
||||
PersistentDataTypeArchive archive = archiveNode.getArchive();
|
||||
|
||||
if (dtm.isChanged()) {
|
||||
if (archive.isChanged()) {
|
||||
if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialogWithCancelAsDefaultButton(
|
||||
null, "Save Archive Changes",
|
||||
"Archive has unsaved changes which must be saved before continuing." +
|
||||
@@ -111,20 +105,14 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
"Save")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
archiveNode.getArchive().save();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Save Archive Failed",
|
||||
"Failed to save changes for Archive: " + dtm.getName() + "\n" + e.getMessage());
|
||||
return;
|
||||
}
|
||||
ArchiveManager archiveManager = plugin.getArchiveManager();
|
||||
archiveManager.save(archive);
|
||||
}
|
||||
|
||||
// TODO: Update message indicating that custom storage specification will not be
|
||||
// retained/permitted (once supported)
|
||||
String msg = "<html>Clear program-architecture for Archive?<BR><font color=\"" +
|
||||
Messages.NORMAL + "\">" + dtm.getPath() +
|
||||
Messages.NORMAL + "\">" + archive.getPath() +
|
||||
"</font><BR> <BR>Archive will revert to using default data organization.";
|
||||
int response = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null,
|
||||
"Confirm Clearing Archive Architecture", msg, "Clear Architecture",
|
||||
@@ -133,25 +121,23 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
return;
|
||||
}
|
||||
|
||||
new TaskLauncher(new ClearProgramArchitectureTask(archiveNode.getArchive(), dtm));
|
||||
new TaskLauncher(new ClearProgramArchitectureTask(archive));
|
||||
}
|
||||
|
||||
private class ClearProgramArchitectureTask extends Task {
|
||||
|
||||
private final Archive archive;
|
||||
private final StandAloneDataTypeManager dtm;
|
||||
private final PersistentDataTypeArchive archive;
|
||||
|
||||
public ClearProgramArchitectureTask(Archive archive, StandAloneDataTypeManager dtm) {
|
||||
public ClearProgramArchitectureTask(PersistentDataTypeArchive archive) {
|
||||
super("Clearing Program-Architecture for Archive", true, false, true, false);
|
||||
this.archive = archive;
|
||||
this.dtm = dtm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
boolean success = false;
|
||||
try {
|
||||
dtm.clearProgramArchitecture(monitor);
|
||||
archive.clearProgramArchitecture(monitor);
|
||||
success = true;
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
@@ -159,25 +145,13 @@ public class ClearArchiveArchitectureAction extends DockingAction {
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, null, "Archive Update Failed",
|
||||
"Failed to clear program-architecture for Archive: " + dtm.getName() + "\n" +
|
||||
"Failed to clear program-architecture for Archive: " + archive.getName() +
|
||||
"\n" +
|
||||
e.getMessage());
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
if (archive instanceof FileArchive) {
|
||||
try {
|
||||
((FileArchive) archive).releaseWriteLock();
|
||||
((FileArchive) archive).acquireWriteLock();
|
||||
}
|
||||
catch (LockException | IOException e) {
|
||||
archive.close();
|
||||
}
|
||||
}
|
||||
else { // if (archive instanceof ProjectArchive) {
|
||||
archive.close();
|
||||
DomainFile df = ((ProjectArchive) archive).getDomainFile();
|
||||
plugin.openArchive(df);
|
||||
}
|
||||
plugin.getArchiveManager().reopenArchive(archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-43
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -18,19 +18,15 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
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.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ArchiveUtils;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.dtarchive.PersistentDataTypeArchive;
|
||||
|
||||
public class CloseArchiveAction extends DockingAction {
|
||||
|
||||
@@ -42,28 +38,25 @@ public class CloseArchiveAction extends DockingAction {
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Close Archive" }, null, "File"));
|
||||
|
||||
setDescription("Closes a data type archive and removes it from the tool "
|
||||
+ "(does not affect program file associations).");
|
||||
setDescription("Closes a data type archive and removes it from the tool " +
|
||||
"(does not affect program file associations).");
|
||||
setEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
if (!(context instanceof DataTypesActionContext dtac)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gtree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
List<GTreeNode> selectedNodes = dtac.getSelectedNodes();
|
||||
|
||||
if (selectionPaths.length == 0) {
|
||||
if (selectedNodes.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
for (GTreeNode node : selectedNodes) {
|
||||
if (!(node instanceof FileArchiveNode) && !(node instanceof InvalidArchiveNode) &&
|
||||
!(node instanceof ProjectArchiveNode)) {
|
||||
return false;
|
||||
@@ -74,38 +67,40 @@ public class CloseArchiveAction extends DockingAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gtree = (GTree) context.getContextObject();
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
DataTypeEditorManager editorManager = plugin.getEditorManager();
|
||||
List<Archive> archives = new ArrayList<Archive>();
|
||||
for (TreePath path : selectionPaths) {
|
||||
Object pathComponent = path.getLastPathComponent();
|
||||
if (pathComponent instanceof InvalidArchiveNode) {
|
||||
InvalidArchiveNode invalidArchiveNode = (InvalidArchiveNode) pathComponent;
|
||||
Archive archive = invalidArchiveNode.getArchive();
|
||||
archive.close();
|
||||
continue;
|
||||
}
|
||||
DataTypesActionContext dtac = (DataTypesActionContext) context;
|
||||
ArchiveManager archiveManager = plugin.getArchiveManager();
|
||||
|
||||
Archive archive = null;
|
||||
Object node = path.getLastPathComponent();
|
||||
if (node instanceof ArchiveNode) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
archive = archiveNode.getArchive();
|
||||
List<PersistentDataTypeArchive> archives = new ArrayList<>();
|
||||
List<GTreeNode> selectedNodes = dtac.getSelectedNodes();
|
||||
for (GTreeNode node : selectedNodes) {
|
||||
if (node instanceof InvalidArchiveNode invalidNode) {
|
||||
archiveManager.closeInvalidArchive(invalidNode.getInvalidArchive());
|
||||
}
|
||||
if (archive != null) {
|
||||
else if (node instanceof ArchiveNode archiveNode) {
|
||||
PersistentDataTypeArchive archive = archiveNode.getArchive();
|
||||
archives.add(archive);
|
||||
if (!editorManager.checkEditors(archive.getDataTypeManager(), true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ArchiveUtils.canClose(archives, gtree)) {
|
||||
for (Archive archive : archives) {
|
||||
editorManager.dismissEditors(archive.getDataTypeManager());
|
||||
archive.close();
|
||||
if (checkEditors(archives)) {
|
||||
return;
|
||||
}
|
||||
for (PersistentDataTypeArchive archive : archives) {
|
||||
if (!plugin.resolveModifiedArchive(archive)) {
|
||||
return;
|
||||
}
|
||||
archiveManager.closeArchive(archive);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkEditors(List<PersistentDataTypeArchive> archives) {
|
||||
DataTypeEditorManager editorManager = plugin.getEditorManager();
|
||||
for (PersistentDataTypeArchive archive : archives) {
|
||||
DataTypeManager dtm = archive.getDataTypeManager();
|
||||
if (!editorManager.checkEditors(dtm, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-54
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -16,31 +15,28 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ArchiveUtils;
|
||||
import ghidra.app.plugin.core.datamgr.archive.FileArchive;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.dtarchive.FileDataTypeArchive;
|
||||
|
||||
public class UnlockArchiveAction extends DockingAction {
|
||||
public static final String ACTION_NAME = "Unlock Archive";
|
||||
/**
|
||||
* Action to set an archive to be read only
|
||||
*/
|
||||
public class CloseArchiveForEditingAction extends DockingAction {
|
||||
public static final String ACTION_NAME = "Close Archive For Editing";
|
||||
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
||||
public UnlockArchiveAction(DataTypeManagerPlugin plugin) {
|
||||
public CloseArchiveForEditingAction(DataTypeManagerPlugin plugin) {
|
||||
super(ACTION_NAME, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
@@ -89,7 +85,7 @@ public class UnlockArchiveAction extends DockingAction {
|
||||
// only valid if all selected paths are file archives
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (hasWriteLock((FileArchiveNode) node)) {
|
||||
if (isModifiable((FileArchiveNode) node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -103,9 +99,9 @@ public class UnlockArchiveAction extends DockingAction {
|
||||
return selectionPaths;
|
||||
}
|
||||
|
||||
private boolean hasWriteLock(FileArchiveNode fileArchiveNode) {
|
||||
FileArchive archive = (FileArchive) fileArchiveNode.getArchive();
|
||||
return archive.hasWriteLock();
|
||||
private boolean isModifiable(FileArchiveNode fileArchiveNode) {
|
||||
FileDataTypeArchive archive = fileArchiveNode.getArchive();
|
||||
return archive.isChangeable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,48 +109,22 @@ public class UnlockArchiveAction extends DockingAction {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
GTreeState treeState = gTree.getTreeState();
|
||||
|
||||
DataTypeEditorManager editorManager = plugin.getEditorManager();
|
||||
for (TreePath path : selectionPaths) {
|
||||
FileArchiveNode node = (FileArchiveNode) path.getLastPathComponent();
|
||||
FileArchive archive = (FileArchive) node.getArchive();
|
||||
if (!canReleaseLock(archive, gTree)) {
|
||||
continue;
|
||||
}
|
||||
FileDataTypeArchive archive = node.getArchive();
|
||||
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
if (!editorManager.checkEditors(dataTypeManager, true)) {
|
||||
return;
|
||||
}
|
||||
// check for changes and save if desired by user. If cancelled, get out.
|
||||
if (!ArchiveUtils.canClose(archive, gTree)) {
|
||||
if (!plugin.resolveModifiedArchive(archive)) {
|
||||
return;
|
||||
}
|
||||
editorManager.dismissEditors(dataTypeManager);
|
||||
|
||||
releaseLock(archive);
|
||||
}
|
||||
gTree.restoreTreeState(treeState);
|
||||
}
|
||||
|
||||
private boolean canReleaseLock(FileArchive archive, Component parent) {
|
||||
if (archive.getFile() == null) {
|
||||
Msg.showInfo(getClass(), parent,
|
||||
"Unsaved Archive", "Unsaved Archives must first be saved.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void releaseLock(FileArchive archive) {
|
||||
try {
|
||||
archive.releaseWriteLock();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
Msg.showError(this, plugin.getProvider().getComponent(),
|
||||
"Unable to Release Write Lock",
|
||||
"Problem attempting to release write lock for archive: " + archive.getName() +
|
||||
"\nMessage: " + ioe.getMessage(), ioe);
|
||||
ArchiveManager archiveManager = plugin.getArchiveManager();
|
||||
archiveManager.reopenFileArchive(archive, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user