mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 20:59:58 +00:00
GP-4341 Force retained checkout if file is in-use during checkin or add-to-version-control. Deprecated upgrade concept during checkin. Revised manner in which file open for update is updated following a version control operation (perform DBHandle update).
This commit is contained in:
parent
74a5b6f0e1
commit
2dff876f0f
@ -32,7 +32,8 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
|
||||
import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent;
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingProposals.ModuleMapProposalGenerator;
|
||||
import ghidra.app.plugin.core.debug.utils.*;
|
||||
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
|
||||
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.async.AsyncDebouncer;
|
||||
import ghidra.async.AsyncTimer;
|
||||
@ -60,6 +61,7 @@ import ghidra.util.datastruct.ListenerSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger static mapping manager",
|
||||
description = "Track and manage static mappings (program-trace relocations)",
|
||||
@ -70,8 +72,9 @@ import ghidra.util.task.TaskMonitor;
|
||||
TraceOpenedPluginEvent.class, TraceClosedPluginEvent.class, },
|
||||
servicesRequired = { ProgramManager.class, DebuggerTraceManagerService.class, },
|
||||
servicesProvided = { DebuggerStaticMappingService.class, })
|
||||
//@formatter:on
|
||||
public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
implements DebuggerStaticMappingService, DomainFolderChangeAdapter {
|
||||
implements DebuggerStaticMappingService, DomainFolderChangeListener {
|
||||
|
||||
protected class MappingEntry {
|
||||
private final TraceStaticMapping mapping;
|
||||
|
@ -22,7 +22,6 @@ import java.util.stream.Collectors;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import ghidra.app.plugin.core.debug.utils.DomainFolderChangeAdapter;
|
||||
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
@ -33,7 +32,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
|
||||
// TODO: Consider making this a front-end plugin?
|
||||
public class ProgramModuleIndexer implements DomainFolderChangeAdapter {
|
||||
public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
public static final String MODULE_PATHS_PROPERTY = "Module Paths";
|
||||
private static final Gson JSON = new Gson();
|
||||
|
||||
@ -288,11 +287,6 @@ public class ProgramModuleIndexer implements DomainFolderChangeAdapter {
|
||||
refreshIndex(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
refreshIndex(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
if (disposed) {
|
||||
|
@ -1,72 +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.debug.utils;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
|
||||
public interface DomainFolderChangeAdapter extends DomainFolderChangeListener {
|
||||
@Override
|
||||
default void domainFileAdded(DomainFile file) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFolderAdded(DomainFolder folder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFolderRemoved(DomainFolder parent, String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileRemoved(DomainFolder parent, String name, String fileID) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFolderRenamed(DomainFolder folder, String oldName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileRenamed(DomainFile file, String oldName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFolderMoved(DomainFolder folder, DomainFolder oldParent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileMoved(DomainFile file, DomainFolder oldParent, String oldName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFolderSetActive(DomainFolder folder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
}
|
||||
}
|
@ -131,11 +131,6 @@ public class ProjectExperimentsTest extends AbstractGhidraHeadedIntegrationTest
|
||||
log("File status changed: file=" + file + " idSet=" + fileIDset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
log("File object replaced: file=" + file + " oldObject=" + obj(oldObject));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
log("File object opened for update: file=" + file + " object=" + obj(object));
|
||||
|
@ -1045,12 +1045,6 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return language.getAddressFactory().getAllAddresses(addrStr, caseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
// TODO: I imagine I'll find out who uses this pretty quick....
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register getRegister(String name) {
|
||||
return language.getRegister(name);
|
||||
|
@ -260,11 +260,6 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||
return view.parseAddress(addrStr, caseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
view.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register getRegister(String name) {
|
||||
return view.getRegister(name);
|
||||
@ -685,7 +680,7 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceProgramView getViewRegisters(TraceThread thread, boolean createIfAbsent) {
|
||||
return view.getViewRegisters(thread, createIfAbsent);
|
||||
public TraceProgramView getViewRegisters(TraceThread t, boolean createIfAbsent) {
|
||||
return view.getViewRegisters(t, createIfAbsent);
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ public class RepositoryFileUpgradeScript extends GhidraScript {
|
||||
dobj.release(this);
|
||||
dobj = null;
|
||||
if (df.isVersioned()) {
|
||||
df.checkin(checkinHandler, false, monitor);
|
||||
df.checkin(checkinHandler, monitor);
|
||||
println("Repository file upgraded: " + df.getPathname());
|
||||
}
|
||||
else {
|
||||
|
@ -1639,22 +1639,6 @@ public class DataTypeManagerHandler {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
if (oldObject instanceof DataTypeArchiveDB) {
|
||||
for (Archive archive : openArchives) {
|
||||
if (archive instanceof ProjectArchive) {
|
||||
ProjectArchive projectArchive = (ProjectArchive) archive;
|
||||
DomainObject domainObject = projectArchive.getDomainObject();
|
||||
if (domainObject == oldObject) {
|
||||
replaceArchiveWithFile(projectArchive, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileRenamed(DomainFile file, String oldName) {
|
||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
||||
|
@ -23,13 +23,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.task.OpenProgramRequest;
|
||||
import ghidra.app.util.task.OpenProgramTask;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -38,7 +34,6 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
/**
|
||||
* Class for tracking open programs in the tool.
|
||||
@ -49,7 +44,6 @@ class MultiProgramManager implements TransactionListener {
|
||||
private PluginTool tool;
|
||||
private ProgramInfo currentInfo;
|
||||
private TransactionMonitor txMonitor;
|
||||
private MyFolderListener folderListener;
|
||||
|
||||
private Runnable programChangedRunnable;
|
||||
private boolean hasUnsavedPrograms;
|
||||
@ -72,8 +66,6 @@ class MultiProgramManager implements TransactionListener {
|
||||
txMonitor = new TransactionMonitor();
|
||||
txMonitor.setName("Transaction Open (Program being modified)");
|
||||
tool.addStatusComponent(txMonitor, true, true);
|
||||
folderListener = new MyFolderListener();
|
||||
tool.getProject().getProjectData().addDomainFolderChangeListener(folderListener);
|
||||
|
||||
programChangedRunnable = () -> {
|
||||
if (tool == null) {
|
||||
@ -111,7 +103,6 @@ class MultiProgramManager implements TransactionListener {
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener);
|
||||
fireActivatedEvent(null);
|
||||
|
||||
for (Program p : programMap.keySet()) {
|
||||
@ -442,41 +433,6 @@ class MultiProgramManager implements TransactionListener {
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
private class MyFolderListener extends DomainFolderListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
|
||||
/**
|
||||
* Special handling for when a file is checked-in. The existing program has be moved
|
||||
* to a proxy file (no longer in the project) so that it can be closed and the program
|
||||
* re-opened with the new version after the check-in merge.
|
||||
*/
|
||||
|
||||
if (!programMap.containsKey(oldObject)) {
|
||||
return;
|
||||
}
|
||||
Element dataState = null;
|
||||
if (currentInfo != null && currentInfo.program == oldObject) {
|
||||
// save dataState as though the project state was saved and re-opened to simulate
|
||||
// recovering after closing the program during this swap
|
||||
dataState = tool.saveDataStateToXml(true);
|
||||
}
|
||||
OpenProgramTask openTask = new OpenProgramTask(file, DomainFile.DEFAULT_VERSION, this);
|
||||
openTask.setSilent();
|
||||
new TaskLauncher(openTask, tool.getToolFrame());
|
||||
OpenProgramRequest openProgramReq = openTask.getOpenProgram();
|
||||
if (openProgramReq != null) {
|
||||
plugin.openProgram(openProgramReq.getProgram(),
|
||||
dataState != null ? ProgramManager.OPEN_CURRENT : ProgramManager.OPEN_VISIBLE);
|
||||
openProgramReq.release();
|
||||
removeProgram((Program) oldObject);
|
||||
if (dataState != null) {
|
||||
tool.restoreDataStateFromXml(dataState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProgramInfo implements Comparable<ProgramInfo> {
|
||||
|
||||
|
@ -132,11 +132,6 @@ public class DomainFolderChangesDisplayPlugin extends Plugin
|
||||
Boolean.toString(fileIDset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
provider.addText("domainFileObjectReplaced: " + file.getPathname());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
provider.addText("domainFileObjectOpenedForUpdate: " + file.getPathname());
|
||||
|
@ -1488,7 +1488,7 @@ public class HeadlessAnalyzer {
|
||||
public boolean createKeepFile() throws CancelledException {
|
||||
return false;
|
||||
}
|
||||
}, true, TaskMonitor.DUMMY);
|
||||
}, TaskMonitor.DUMMY);
|
||||
Msg.info(this, "REPORT: Committed file changes to repository: " + df.getPathname());
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -701,20 +701,6 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
|
||||
oldParent.getPathname(), oldName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
// not tested
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// not tested
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// not tested
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ public class FakeSharedProject {
|
||||
private GhidraProject gProject;
|
||||
private TestProgramManager programManager = new TestProgramManager();
|
||||
private FakeRepository repo;
|
||||
private boolean isFileSharingEnabled; // set true if multiple projects share repo files
|
||||
|
||||
public FakeSharedProject(FakeRepository repo, User user) throws IOException {
|
||||
|
||||
@ -85,6 +86,14 @@ public class FakeSharedProject {
|
||||
invokeInstanceMethod("setVersionedFileSystem", pd, argTypes(FileSystem.class), args(fs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark project as sharing file with another project via a common repo.
|
||||
* This is needed to bypass check performed by assertFileInProject
|
||||
*/
|
||||
public void enableFileSharing() {
|
||||
isFileSharingEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ghidra project
|
||||
* @return the ghidra project
|
||||
@ -162,6 +171,7 @@ public class FakeSharedProject {
|
||||
public DomainFile getDomainFile(String filepath) {
|
||||
Project project = getGhidraProject().getProject();
|
||||
ProjectData projectData = project.getProjectData();
|
||||
refresh(); // force refresh since we do not employ repo listener
|
||||
DomainFile df;
|
||||
if (filepath.startsWith("/")) {
|
||||
df = projectData.getFile(filepath);
|
||||
@ -287,7 +297,7 @@ public class FakeSharedProject {
|
||||
}
|
||||
};
|
||||
|
||||
df.checkin(ch, false, TaskMonitor.DUMMY);
|
||||
df.checkin(ch, TaskMonitor.DUMMY);
|
||||
repo.refresh();
|
||||
}
|
||||
|
||||
@ -387,6 +397,10 @@ public class FakeSharedProject {
|
||||
throw new IllegalArgumentException("DomainFile cannot be null");
|
||||
}
|
||||
|
||||
if (isFileSharingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectLocator pl = df.getProjectLocator();
|
||||
ProjectLocator mypl = getProjectData().getProjectLocator();
|
||||
if (!pl.equals(mypl)) {
|
||||
|
@ -21,12 +21,13 @@ import javax.swing.Icon;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.buffers.BufferFile;
|
||||
import db.buffers.LocalManagedBufferFile;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.data.DBContentHandler;
|
||||
import ghidra.framework.data.DomainObjectMergeManager;
|
||||
import ghidra.framework.data.*;
|
||||
import ghidra.framework.model.ChangeSet;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.framework.store.local.LocalDatabaseItem;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
@ -168,4 +169,20 @@ public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canResetDBSourceFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetDBSourceFile(FolderItem item, DomainObjectAdapterDB domainObj)
|
||||
throws IOException {
|
||||
if (!(item instanceof LocalDatabaseItem dbItem) ||
|
||||
!(domainObj instanceof VTSessionDB vtSession)) {
|
||||
throw new IllegalArgumentException("LocalDatabaseItem and VTSessionDB required");
|
||||
}
|
||||
LocalManagedBufferFile bf = dbItem.openForUpdate(FolderItem.DEFAULT_CHECKOUT_ID);
|
||||
vtSession.getDBHandle().setDBVersionedSourceFile(bf);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.feature.vt.api.db.VTAssociationDB;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.api.util.VTSessionFileUtil;
|
||||
import ghidra.feature.vt.gui.duallisting.VTListingContext;
|
||||
import ghidra.feature.vt.gui.provider.markuptable.VTMarkupItemContext;
|
||||
import ghidra.feature.vt.gui.task.SaveTask;
|
||||
@ -69,7 +68,6 @@ public class VTControllerImpl
|
||||
|
||||
private ToolOptions vtOptions;
|
||||
private MatchInfo currentMatchInfo;
|
||||
private MyFolderListener folderListener;
|
||||
|
||||
public VTControllerImpl(VTPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -77,11 +75,6 @@ public class VTControllerImpl
|
||||
matchInfoFactory = new MatchInfoFactory();
|
||||
vtOptions = plugin.getTool().getOptions(VERSION_TRACKING_OPTIONS_NAME);
|
||||
vtOptions.addOptionsChangeListener(this);
|
||||
folderListener = new MyFolderListener();
|
||||
plugin.getTool()
|
||||
.getProject()
|
||||
.getProjectData()
|
||||
.addDomainFolderChangeListener(folderListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -763,82 +756,6 @@ public class VTControllerImpl
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private void updateProgram(DomainFile file, boolean isSource) {
|
||||
|
||||
String type = isSource ? "Source" : "Destination";
|
||||
Program newProgram;
|
||||
try {
|
||||
newProgram = (Program) file.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, getParentComponent(),
|
||||
"Error opening VT " + type + " Program: " + file, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSource) {
|
||||
session.updateSourceProgram(newProgram);
|
||||
}
|
||||
else {
|
||||
session.updateDestinationProgram(newProgram);
|
||||
}
|
||||
|
||||
// List<DomainObjectChangeRecord> events = new ArrayList<DomainObjectChangeRecord>();
|
||||
// events.add(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
// domainObjectChanged(new DomainObjectChangedEvent(newProgram, events));
|
||||
matchInfoFactory.clearCache();
|
||||
fireSessionChanged();
|
||||
}
|
||||
|
||||
private class MyFolderListener extends DomainFolderListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.getSourceProgram() == oldObject) {
|
||||
updateProgram(file, true);
|
||||
return;
|
||||
}
|
||||
|
||||
String type;
|
||||
if (session == oldObject) {
|
||||
type = "VT Session";
|
||||
}
|
||||
else if (session.getDestinationProgram() == oldObject) {
|
||||
if (VTSessionFileUtil.canUpdate(file)) {
|
||||
updateProgram(file, false);
|
||||
return;
|
||||
}
|
||||
type = "Destination Program";
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Session or destination program can no longer be saved to project so we
|
||||
// have no choice but to close session.
|
||||
|
||||
// Since we are already in the Swing thread we need to delay closing so we do
|
||||
// not continue to block the Swing thread and the checkin which is in progress.
|
||||
// This allows the DomainFile checkin to complete its processing first.
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
Msg.showInfo(this, plugin.getTool().getToolFrame(), "Closing VT Session",
|
||||
type + " checkin has forced session close.\n" +
|
||||
"You will be prompted to save any other changes if needed, after which\n" +
|
||||
"you may reopen the VT Session.");
|
||||
|
||||
closeVersionTrackingSession();
|
||||
|
||||
// NOTE: a future convenience could be added to attempt reopening of session
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class OpenSessionTask extends Task {
|
||||
|
||||
private final VTSession newSession;
|
||||
|
@ -18,7 +18,6 @@ package db;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
|
||||
import db.buffers.*;
|
||||
import db.util.ErrorHandler;
|
||||
@ -439,8 +438,7 @@ public class DBHandle {
|
||||
* @throws IllegalStateException if transaction is already active or this {@link DBHandle} has
|
||||
* already been closed.
|
||||
*/
|
||||
public Transaction openTransaction(ErrorHandler errorHandler)
|
||||
throws IllegalStateException {
|
||||
public Transaction openTransaction(ErrorHandler errorHandler) throws IllegalStateException {
|
||||
return new Transaction() {
|
||||
|
||||
long txId = startTransaction();
|
||||
@ -542,6 +540,33 @@ public class DBHandle {
|
||||
return (bufferMgr != null && !bufferMgr.atCheckpoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB source buffer file with a newer local buffer file version.
|
||||
* Intended for use following a merge or commit operation only where a local checkout has been
|
||||
* retained.
|
||||
* @param versionedSourceBufferFile updated local DB source buffer file opened for versioning
|
||||
* update (NOTE: file itself is read-only). File must be an instance of
|
||||
* {@link LocalManagedBufferFile}.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void setDBVersionedSourceFile(BufferFile versionedSourceBufferFile) throws IOException {
|
||||
if (!(versionedSourceBufferFile instanceof LocalManagedBufferFile bf) ||
|
||||
!versionedSourceBufferFile.isReadOnly()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Requires local versioned buffer file opened for versioning update");
|
||||
}
|
||||
synchronized (this) {
|
||||
if (isTransactionActive()) {
|
||||
throw new IOException("transaction is active");
|
||||
}
|
||||
bufferMgr.clearRecoveryFiles();
|
||||
bufferMgr.setDBVersionedSourceFile(bf);
|
||||
++checkpointNum;
|
||||
reloadTables();
|
||||
}
|
||||
notifyDbRestored();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate current transaction. If commit is false a rollback may occur followed by
|
||||
* {@link DBListener#dbRestored(DBHandle)} notification to listeners. This method is very
|
||||
@ -1022,10 +1047,9 @@ public class DBHandle {
|
||||
public Table[] getTables() {
|
||||
Table[] t = new Table[tables.size()];
|
||||
|
||||
Iterator<Table> it = tables.values().iterator();
|
||||
int i = 0;
|
||||
while (it.hasNext()) {
|
||||
t[i++] = it.next();
|
||||
for (Table element : tables.values()) {
|
||||
t[i++] = element;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
@ -1050,8 +1074,7 @@ public class DBHandle {
|
||||
* @return new table instance
|
||||
* @throws IOException if IO error occurs during table creation
|
||||
*/
|
||||
public Table createTable(String name, Schema schema, int[] indexedColumns)
|
||||
throws IOException {
|
||||
public Table createTable(String name, Schema schema, int[] indexedColumns) throws IOException {
|
||||
Table table;
|
||||
synchronized (this) {
|
||||
if (tables.containsKey(name)) {
|
||||
|
@ -223,6 +223,22 @@ public class BufferMgr {
|
||||
approxCacheSize < MINIMUM_CACHE_SIZE ? MINIMUM_CACHE_SIZE : approxCacheSize;
|
||||
maxCacheSize = (int) (approxCacheSize / bufferSize);
|
||||
|
||||
// Setup baseline - checkpoint 0
|
||||
startCheckpoint();
|
||||
baselineCheckpointHead = currentCheckpointHead;
|
||||
currentCheckpointHead = null;
|
||||
|
||||
initializeCache();
|
||||
|
||||
addInstance(this);
|
||||
}
|
||||
|
||||
private void initializeCache() throws IOException {
|
||||
|
||||
if (cacheFile != null) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
|
||||
// Setup memory cache list
|
||||
cacheHead = new BufferNode(HEAD, -1);
|
||||
cacheTail = new BufferNode(TAIL, -1);
|
||||
@ -234,11 +250,6 @@ public class BufferMgr {
|
||||
|
||||
cacheIndexProvider = new IndexProvider();
|
||||
|
||||
// Setup baseline - checkpoint 0
|
||||
startCheckpoint();
|
||||
baselineCheckpointHead = currentCheckpointHead;
|
||||
currentCheckpointHead = null;
|
||||
|
||||
// Copy file parameters into cache file
|
||||
if (sourceFile != null) {
|
||||
String[] parmNames = sourceFile.getParameterNames();
|
||||
@ -247,8 +258,6 @@ public class BufferMgr {
|
||||
}
|
||||
}
|
||||
|
||||
addInstance(this);
|
||||
|
||||
if (alwaysPreCache) {
|
||||
startPreCacheIfNeeded();
|
||||
}
|
||||
@ -417,9 +426,7 @@ public class BufferMgr {
|
||||
|
||||
// Dispose all buffer nodes - speeds up garbage collection
|
||||
if (checkpointHeads != null) {
|
||||
Iterator<BufferNode> iter = checkpointHeads.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BufferNode node = iter.next();
|
||||
for (BufferNode node : checkpointHeads) {
|
||||
while (node != null) {
|
||||
BufferNode next = node.nextInCheckpoint;
|
||||
node.buffer = null;
|
||||
@ -435,9 +442,7 @@ public class BufferMgr {
|
||||
checkpointHeads = null;
|
||||
}
|
||||
if (redoCheckpointHeads != null) {
|
||||
Iterator<BufferNode> iter = redoCheckpointHeads.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BufferNode node = iter.next();
|
||||
for (BufferNode node : redoCheckpointHeads) {
|
||||
while (node != null) {
|
||||
BufferNode next = node.nextInCheckpoint;
|
||||
node.buffer = null;
|
||||
@ -702,24 +707,7 @@ public class BufferMgr {
|
||||
return; // only pre-cache remote buffer files
|
||||
}
|
||||
synchronized (preCacheLock) {
|
||||
preCacheThread = new Thread(() -> {
|
||||
try {
|
||||
preCacheSourceFile();
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
synchronized (preCacheLock) {
|
||||
preCacheStatus = PreCacheStatus.STOPPED;
|
||||
preCacheThread = null;
|
||||
preCacheLock.notifyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
preCacheThread = new Thread(() -> preCacheSourceFile());
|
||||
preCacheThread.setName("Pre-Cache");
|
||||
preCacheThread.setPriority(Thread.MIN_PRIORITY);
|
||||
preCacheThread.start();
|
||||
@ -731,23 +719,38 @@ public class BufferMgr {
|
||||
* Pre-cache source file into cache file. This is intended to be run in a
|
||||
* dedicated thread when the source file is remote.
|
||||
*/
|
||||
private void preCacheSourceFile() throws IOException {
|
||||
if (!(sourceFile instanceof BufferFileAdapter)) {
|
||||
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache started...");
|
||||
int cacheCount = 0;
|
||||
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
|
||||
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
|
||||
BufferFileBlock block;
|
||||
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
|
||||
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
|
||||
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
|
||||
++cacheCount;
|
||||
}
|
||||
private void preCacheSourceFile() {
|
||||
try {
|
||||
if (!(sourceFile instanceof BufferFileAdapter)) {
|
||||
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache started...");
|
||||
int cacheCount = 0;
|
||||
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
|
||||
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
|
||||
BufferFileBlock block;
|
||||
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
|
||||
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
|
||||
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
|
||||
++cacheCount;
|
||||
}
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
|
||||
sourceFile.getIndexCount() + " buffers to cache");
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
synchronized (preCacheLock) {
|
||||
preCacheStatus = PreCacheStatus.STOPPED;
|
||||
preCacheThread = null;
|
||||
preCacheLock.notifyAll();
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
|
||||
sourceFile.getIndexCount() + " buffers to cache");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1744,6 +1747,51 @@ public class BufferMgr {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source buffer file with a newer local buffer file version.
|
||||
* Intended for use following a merge or commit operation only where a local checkout has been
|
||||
* retained.
|
||||
* @param versionedSourceBufferFile updated local source buffer file opened for versioning
|
||||
* update (NOTE: file itself is read-only).
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void setDBVersionedSourceFile(LocalManagedBufferFile versionedSourceBufferFile)
|
||||
throws IOException {
|
||||
|
||||
synchronized (snapshotLock) {
|
||||
synchronized (this) {
|
||||
if (!(sourceFile instanceof LocalManagedBufferFile)) {
|
||||
throw new UnsupportedOperationException(getClass().getSimpleName() +
|
||||
".setDBSourceFile not allowed: " + sourceFile.getClass());
|
||||
}
|
||||
if (bufferSize != sourceFile.getBufferSize()) {
|
||||
throw new IllegalArgumentException("Buffer size mismatch");
|
||||
}
|
||||
|
||||
if (corruptedState) {
|
||||
throw new IOException("Corrupted BufferMgr state");
|
||||
}
|
||||
|
||||
if (lockCount != 0) {
|
||||
throw new IOException("Attempted checkout update while buffers are locked");
|
||||
}
|
||||
|
||||
stopPreCache();
|
||||
|
||||
clearCheckpoints();
|
||||
|
||||
doSetSourceFile(versionedSourceBufferFile);
|
||||
|
||||
// re-initialize cached file data
|
||||
int cnt = sourceFile.getIndexCount();
|
||||
indexProvider = new IndexProvider(cnt, sourceFile.getFreeIndexes());
|
||||
bufferTable = new ObjectArray(cnt + INITIAL_BUFFER_TABLE_SIZE);
|
||||
|
||||
initializeCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current set of buffers to a new version of the source buffer file.
|
||||
* If the buffer manager was not instantiated with a source file an
|
||||
@ -1825,7 +1873,7 @@ public class BufferMgr {
|
||||
monitor.setCancelEnabled(oldCancelState & !monitor.isCancelled());
|
||||
}
|
||||
|
||||
setSourceFile(outFile);
|
||||
doSetSourceFile(outFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1881,7 +1929,7 @@ public class BufferMgr {
|
||||
monitor.setCancelEnabled(true);
|
||||
}
|
||||
if (associateWithNewFile) {
|
||||
setSourceFile(outFile);
|
||||
doSetSourceFile(outFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1965,7 +2013,7 @@ public class BufferMgr {
|
||||
}
|
||||
}
|
||||
|
||||
private void setSourceFile(BufferFile newFile) {
|
||||
private void doSetSourceFile(BufferFile newFile) {
|
||||
|
||||
// Close buffer file
|
||||
if (sourceFile != null) {
|
||||
|
@ -157,7 +157,6 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
||||
* <code>preSaveThread</code> corresponds to the PreSaveTask which creates the
|
||||
* preSaveFile when this buffer file is updateable.
|
||||
*/
|
||||
//private Thread preSaveThread;
|
||||
private PreSaveTask preSaveTask;
|
||||
|
||||
/**
|
||||
@ -228,8 +227,8 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
||||
setFreeIndexes(versionFileHandler.getFreeIndexList());
|
||||
String[] names = versionFileHandler.getOldParameterNames();
|
||||
clearParameters();
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
setParameter(names[i], versionFileHandler.getOldParameter(names[i]));
|
||||
for (String name : names) {
|
||||
setParameter(name, versionFileHandler.getOldParameter(name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ package ghidra.framework.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
@ -83,9 +84,8 @@ public interface ContentHandler<T extends DomainObjectAdapter> extends Extension
|
||||
* @throws VersionException if unable to handle file content due to version
|
||||
* difference which could not be handled.
|
||||
*/
|
||||
T getImmutableObject(FolderItem item, Object consumer, int version,
|
||||
int minChangeVersion, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException;
|
||||
T getImmutableObject(FolderItem item, Object consumer, int version, int minChangeVersion,
|
||||
TaskMonitor monitor) throws IOException, CancelledException, VersionException;
|
||||
|
||||
/**
|
||||
* Open a folder item for read-only use. While changes are permitted on the
|
||||
@ -104,9 +104,8 @@ public interface ContentHandler<T extends DomainObjectAdapter> extends Extension
|
||||
* @throws VersionException if unable to handle file content due to version
|
||||
* difference which could not be handled.
|
||||
*/
|
||||
T getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade,
|
||||
Object consumer, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException;
|
||||
T getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade, Object consumer,
|
||||
TaskMonitor monitor) throws IOException, VersionException, CancelledException;
|
||||
|
||||
/**
|
||||
* Open a folder item for update. Changes made to the returned object may be
|
||||
@ -127,8 +126,8 @@ public interface ContentHandler<T extends DomainObjectAdapter> extends Extension
|
||||
* @throws VersionException if unable to handle file content due to version
|
||||
* difference which could not be handled.
|
||||
*/
|
||||
T getDomainObject(FolderItem item, FileSystem userfs, long checkoutId,
|
||||
boolean okToUpgrade, boolean okToRecover, Object consumer, TaskMonitor monitor)
|
||||
T getDomainObject(FolderItem item, FileSystem userfs, long checkoutId, boolean okToUpgrade,
|
||||
boolean okToRecover, Object consumer, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException;
|
||||
|
||||
/**
|
||||
@ -204,4 +203,47 @@ public interface ContentHandler<T extends DomainObjectAdapter> extends Extension
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this content handler supports the use of
|
||||
* {@link #resetDBSourceFile(FolderItem, DomainObjectAdapterDB)} .
|
||||
* <p>
|
||||
* A versioned {@link DomainObjectAdapterDB domain object} open for update may have its
|
||||
* underlying database reset to the latest buffer file version:
|
||||
* <ol>
|
||||
* <li>The {@link #resetDBSourceFile(FolderItem, DomainObjectAdapterDB)} method is
|
||||
* invoked (synchronized on filesystem) to reset the underlying database source file and
|
||||
* and any corresponding change sets held by the specified domain object to the latest
|
||||
* version,</li>
|
||||
* <li>afterwhich the caller must {@link DomainObjectAdapter#invalidate() invalidate} the domain
|
||||
* object instance which will clear all caches and generate a {@link DomainObjectEvent#RESTORED}
|
||||
* event.</li>
|
||||
* </ol>
|
||||
* @return true if this content handler supports DB source file replacement, else false
|
||||
*/
|
||||
public default boolean canResetDBSourceFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the database for the specified domain object to its latest buffer file version.
|
||||
* It is very important that the specified folder item matches the item which was used to
|
||||
* originally open the specified domain object. This method should be invoked with a
|
||||
* filesystem lock.
|
||||
* <p>
|
||||
* Following the invocation of this method, the specified domain object should be
|
||||
* {@link DomainObjectAdapter#invalidate() invalidated} without a filesystem lock.
|
||||
*
|
||||
* @param item local versioned database folder item currently checked-out. An error will be
|
||||
* thrown if not an instanceof LocalDatabaseItem. This should always be the case for an item
|
||||
* which has just processed a versioning action with a retained checkout (e.g., checkin,
|
||||
* merge, add-to-version-control).
|
||||
* @param domainObj domain object which is currently open for update
|
||||
* @throws IOException if an IO error occurs
|
||||
* @throws IllegalArgumentException if invalid or unsupported arguments are provided
|
||||
*/
|
||||
public default void resetDBSourceFile(FolderItem item, DomainObjectAdapterDB domainObj)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ package ghidra.framework.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.buffers.ManagedBufferFile;
|
||||
import ghidra.framework.store.*;
|
||||
@ -55,9 +57,8 @@ public abstract class DBContentHandler<T extends DomainObjectAdapterDB>
|
||||
FileSystem fs, String path, String name, TaskMonitor monitor)
|
||||
throws InvalidNameException, CancelledException, IOException {
|
||||
DBHandle dbh = domainObj.getDBHandle();
|
||||
ManagedBufferFile bf =
|
||||
fs.createDatabase(path, name, FileIDFactory.createFileID(), contentType,
|
||||
dbh.getBufferSize(), SystemUtilities.getUserName(), null);
|
||||
ManagedBufferFile bf = fs.createDatabase(path, name, FileIDFactory.createFileID(),
|
||||
contentType, dbh.getBufferSize(), SystemUtilities.getUserName(), null);
|
||||
long checkoutId = bf.getCheckinID(); // item remains checked-out after saveAs
|
||||
boolean success = false;
|
||||
try {
|
||||
|
@ -21,7 +21,7 @@ import ghidra.util.exception.CancelledException;
|
||||
/**
|
||||
* <code>DefaultCheckinHandler</code> provides a simple
|
||||
* check-in handler for use with
|
||||
* {@link DomainFile#checkin(CheckinHandler, boolean, ghidra.util.task.TaskMonitor)}
|
||||
* {@link DomainFile#checkin(CheckinHandler, ghidra.util.task.TaskMonitor)}
|
||||
*/
|
||||
public class DefaultCheckinHandler implements CheckinHandler {
|
||||
|
||||
|
@ -181,57 +181,31 @@ class DomainFileIndex implements DomainFolderChangeListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileAdded(DomainFile file) {
|
||||
updateFileEntry((GhidraFile) file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileMoved(DomainFile file, DomainFolder oldParent, String oldName) {
|
||||
updateFileEntry((GhidraFile) file);
|
||||
}
|
||||
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileRemoved(DomainFolder parent, String name, String fileID) {
|
||||
fileIdToPathIndex.remove(fileID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileRenamed(DomainFile file, String oldName) {
|
||||
updateFileEntry((GhidraFile) file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
if (fileIDset) {
|
||||
updateFileEntry((GhidraFile) file);
|
||||
}
|
||||
}
|
||||
|
||||
public void domainFolderAdded(DomainFolder folder) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFolderMoved(DomainFolder folder, DomainFolder oldParent) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFolderRemoved(DomainFolder parent, String name) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFolderRenamed(DomainFolder folder, String oldName) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void domainFolderSetActive(DomainFolder folder) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ public class DomainFileProxy implements DomainFile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
public void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
throw new UnsupportedOperationException("Repository operations not supported");
|
||||
}
|
||||
|
@ -26,8 +26,7 @@ class DomainFolderChangeListenerList implements DomainFolderChangeListener {
|
||||
private DomainFileIndex fileIndex;
|
||||
|
||||
/** CopyOnWriteArrayList prevents the need for synchronization */
|
||||
private List<DomainFolderChangeListener> list =
|
||||
new CopyOnWriteArrayList<>();
|
||||
private List<DomainFolderChangeListener> list = new CopyOnWriteArrayList<>();
|
||||
|
||||
DomainFolderChangeListenerList(DomainFileIndex fileIndex) {
|
||||
this.fileIndex = fileIndex;
|
||||
@ -199,19 +198,6 @@ class DomainFolderChangeListenerList implements DomainFolderChangeListener {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(final DomainFile file, final DomainObject oldObject) {
|
||||
fileIndex.domainFileObjectReplaced(file, oldObject);
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Swing.runNow(() -> {
|
||||
for (DomainFolderChangeListener listener : list) {
|
||||
listener.domainFileObjectReplaced(file, oldObject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
list.clear();
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ public abstract class DomainObjectAdapter implements DomainObject {
|
||||
* with consumer.
|
||||
*
|
||||
* @param name name of the object
|
||||
* @param timeInterval the time (in milliseconds) to wait before the event queue is flushed. If
|
||||
* a new event comes in before the time expires, the timer is reset.
|
||||
* @param timeInterval the time (in milliseconds) to wait before the event queue is flushed.
|
||||
* If a new event comes in before the time expires the timer is reset.
|
||||
* @param consumer the object that created this domain object
|
||||
*/
|
||||
protected DomainObjectAdapter(String name, int timeInterval, Object consumer) {
|
||||
@ -98,6 +98,15 @@ public abstract class DomainObjectAdapter implements DomainObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates any caching in a program and generate a {@link DomainObjectEvent#RESTORED}
|
||||
* event.
|
||||
* NOTE: Over-using this method can adversely affect system performance.
|
||||
*/
|
||||
public void invalidate() {
|
||||
fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(Object consumer) {
|
||||
synchronized (consumers) {
|
||||
|
@ -37,32 +37,6 @@ import ghidra.util.task.TaskMonitor;
|
||||
* concept of starting a transaction before a change is made to the
|
||||
* domain object and ending the transaction. The transaction allows for
|
||||
* undo/redo changes.
|
||||
*
|
||||
* The implementation class must also satisfy the following requirements:
|
||||
* <pre>
|
||||
*
|
||||
* 1. The following constructor signature must be implemented:
|
||||
*
|
||||
* **
|
||||
* * Constructs new Domain Object
|
||||
* * @param dbh a handle to an open domain object database.
|
||||
* * @param openMode one of:
|
||||
* * READ_ONLY: the original database will not be modified
|
||||
* * UPDATE: the database can be written to.
|
||||
* * UPGRADE: the database is upgraded to the latest schema as it is opened.
|
||||
* * @param monitor TaskMonitor that allows the open to be cancelled.
|
||||
* * @param consumer the object that keeping the program open.
|
||||
* *
|
||||
* * @throws IOException if an error accessing the database occurs.
|
||||
* * @throws VersionException if database version does not match implementation. UPGRADE may be possible.
|
||||
* **
|
||||
* public DomainObjectAdapterDB(DBHandle dbh, int openMode, TaskMonitor monitor, Object consumer) throws IOException, VersionException
|
||||
*
|
||||
* 2. The following static field must be provided:
|
||||
*
|
||||
* public static final String CONTENT_TYPE
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
implements ErrorHandler, DBConstants {
|
||||
@ -100,12 +74,10 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
|
||||
/**
|
||||
* Construct a new DomainObjectAdapterDB object.
|
||||
* If construction of this object fails, be sure to release with consumer
|
||||
* @param dbh database handle
|
||||
* @param name name of the domain object
|
||||
* @param timeInterval the time (in milliseconds) to wait before the
|
||||
* event queue is flushed. If a new event comes in before the time expires,
|
||||
* the timer is reset.
|
||||
* @param timeInterval the time (in milliseconds) to wait before the event queue is flushed.
|
||||
* If a new event comes in before the time expires the timer is reset.
|
||||
* @param consumer the object that created this domain object
|
||||
*/
|
||||
protected DomainObjectAdapterDB(DBHandle dbh, String name, int timeInterval, Object consumer) {
|
||||
@ -129,6 +101,7 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
* prior to closing a transaction.
|
||||
*/
|
||||
public void flushWriteCache() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,6 +110,7 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
* prior to aborting a transaction.
|
||||
*/
|
||||
public void invalidateWriteCache() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,6 +465,12 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
transactionMgr.clearUndo(notifyListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
super.invalidate();
|
||||
}
|
||||
|
||||
protected void clearCache(boolean all) {
|
||||
options.clearCache();
|
||||
}
|
||||
|
@ -382,10 +382,11 @@ public class GhidraFile implements DomainFile {
|
||||
@Override
|
||||
public boolean canAddToRepository() {
|
||||
try {
|
||||
return getFileData().canAddToRepository();
|
||||
getFileData().checkCanAddToRepository();
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
fileError(e);
|
||||
// ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -473,10 +474,9 @@ public class GhidraFile implements DomainFile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
public void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
getFileData().checkin(checkinHandler, okToUpgrade,
|
||||
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||
getFileData().checkin(checkinHandler, monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -436,9 +436,8 @@ public class GhidraFileData {
|
||||
ChangeSet getChangesByOthersSinceCheckout() throws VersionException, IOException {
|
||||
synchronized (fileSystem) {
|
||||
if (versionedFolderItem != null && folderItem != null && folderItem.isCheckedOut()) {
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
return ch.getChangeSet(versionedFolderItem, folderItem.getCheckoutVersion(),
|
||||
versionedFolderItem.getCurrentVersion());
|
||||
return getContentHandler().getChangeSet(versionedFolderItem,
|
||||
folderItem.getCheckoutVersion(), versionedFolderItem.getCurrentVersion());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -478,6 +477,12 @@ public class GhidraFileData {
|
||||
*/
|
||||
DomainObject getDomainObject(Object consumer, boolean okToUpgrade, boolean okToRecover,
|
||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||
|
||||
// Don't allow this call while versioning operation is on-going
|
||||
if (busy.get()) {
|
||||
throw new FileInUseException("Cannot open during versioning operation");
|
||||
}
|
||||
|
||||
FolderItem myFolderItem;
|
||||
DomainObjectAdapter domainObj = null;
|
||||
synchronized (fileSystem) {
|
||||
@ -494,9 +499,9 @@ public class GhidraFileData {
|
||||
return domainObj;
|
||||
}
|
||||
}
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
if (folderItem == null) {
|
||||
DomainObjectAdapter doa = ch.getReadOnlyObject(versionedFolderItem,
|
||||
DomainObjectAdapter doa = contentHandler.getReadOnlyObject(versionedFolderItem,
|
||||
DomainFile.DEFAULT_VERSION, true, consumer, monitor);
|
||||
doa.setChanged(false);
|
||||
DomainFileProxy proxy = new DomainFileProxy(name, parent.getPathname(), doa,
|
||||
@ -506,7 +511,7 @@ public class GhidraFileData {
|
||||
}
|
||||
myFolderItem = folderItem;
|
||||
|
||||
domainObj = ch.getDomainObject(myFolderItem, parent.getUserFileSystem(),
|
||||
domainObj = contentHandler.getDomainObject(myFolderItem, parent.getUserFileSystem(),
|
||||
FolderItem.DEFAULT_CHECKOUT_ID, okToUpgrade, okToRecover, consumer, monitor);
|
||||
projectData.setDomainObject(getPathname(), domainObj);
|
||||
|
||||
@ -567,8 +572,8 @@ public class GhidraFileData {
|
||||
FolderItem item =
|
||||
(folderItem != null && version == DomainFile.DEFAULT_VERSION) ? folderItem
|
||||
: versionedFolderItem;
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
DomainObjectAdapter doa = ch.getReadOnlyObject(item, version, true, consumer, monitor);
|
||||
DomainObjectAdapter doa =
|
||||
getContentHandler().getReadOnlyObject(item, version, true, consumer, monitor);
|
||||
doa.setChanged(false);
|
||||
|
||||
// Notify file manager of in-use domain object.
|
||||
@ -606,13 +611,14 @@ public class GhidraFileData {
|
||||
throws VersionException, IOException, CancelledException {
|
||||
synchronized (fileSystem) {
|
||||
DomainObjectAdapter obj = null;
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
if (versionedFolderItem == null ||
|
||||
(version == DomainFile.DEFAULT_VERSION && folderItem != null) || isHijacked()) {
|
||||
obj = ch.getImmutableObject(folderItem, consumer, version, -1, monitor);
|
||||
obj = contentHandler.getImmutableObject(folderItem, consumer, version, -1, monitor);
|
||||
}
|
||||
else {
|
||||
obj = ch.getImmutableObject(versionedFolderItem, consumer, version, -1, monitor);
|
||||
obj = contentHandler.getImmutableObject(versionedFolderItem, consumer, version, -1,
|
||||
monitor);
|
||||
}
|
||||
|
||||
// Notify file manager of in-use domain object.
|
||||
@ -870,33 +876,6 @@ public class GhidraFileData {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this private file may be added to the associated repository.
|
||||
* @return true if can add to the repository
|
||||
*/
|
||||
boolean canAddToRepository() {
|
||||
synchronized (fileSystem) {
|
||||
try {
|
||||
if (fileSystem.isReadOnly() || versionedFileSystem.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
if (folderItem == null || versionedFolderItem != null) {
|
||||
return false;
|
||||
}
|
||||
if (folderItem.isCheckedOut()) {
|
||||
return false;
|
||||
}
|
||||
if (isLinkFile()) {
|
||||
return GhidraURL.isServerRepositoryURL(LinkHandler.getURL(folderItem));
|
||||
}
|
||||
return !getContentHandler().isPrivateContentType();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this file may be checked-out from the associated repository.
|
||||
* User's with read-only repository access will not have checkout ability.
|
||||
@ -1021,6 +1000,32 @@ public class GhidraFileData {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform neccessary check to ensure this file may be added to version control.
|
||||
* @throws IOException if any checks fail or other IO error occurs
|
||||
*/
|
||||
void checkCanAddToRepository() throws IOException {
|
||||
if (!versionedFileSystem.isOnline()) {
|
||||
throw new NotConnectedException("Not connected to repository server");
|
||||
}
|
||||
if (fileSystem.isReadOnly() || versionedFileSystem.isReadOnly()) {
|
||||
throw new ReadOnlyException(
|
||||
"versioning permitted within writeable project and repository only");
|
||||
}
|
||||
if (folderItem == null) {
|
||||
throw new FileNotFoundException("File not found");
|
||||
}
|
||||
if (folderItem.isCheckedOut() || versionedFolderItem != null) {
|
||||
throw new IOException("File already versioned");
|
||||
}
|
||||
if (isLinkFile() && !GhidraURL.isServerRepositoryURL(LinkHandler.getURL(folderItem))) {
|
||||
throw new IOException("Local project link-file may not be versioned");
|
||||
}
|
||||
if (getContentHandler().isPrivateContentType()) {
|
||||
throw new IOException("Content may not be versioned: " + getContentType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this private file to version control.
|
||||
* @param comment new version comment
|
||||
@ -1033,76 +1038,87 @@ public class GhidraFileData {
|
||||
*/
|
||||
void addToVersionControl(String comment, boolean keepCheckedOut, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
DomainObjectAdapter oldDomainObj = null;
|
||||
synchronized (fileSystem) {
|
||||
if (!canAddToRepository()) {
|
||||
if (fileSystem.isReadOnly() || versionedFileSystem.isReadOnly()) {
|
||||
throw new ReadOnlyException(
|
||||
"addToVersionControl permitted within writeable project and repository only");
|
||||
}
|
||||
throw new IOException("addToVersionControl not allowed for file");
|
||||
}
|
||||
|
||||
checkCanAddToRepository();
|
||||
|
||||
if (busy.getAndSet(true)) {
|
||||
throw new FileInUseException(name + " is busy");
|
||||
}
|
||||
|
||||
DomainObjectAdapterDB inUseDomainObj = null;
|
||||
projectData.mergeStarted();
|
||||
try {
|
||||
inUseDomainObj = getAndLockInUseDomainObjectForMergeUpdate("checkin");
|
||||
|
||||
if (isLinkFile()) {
|
||||
keepCheckedOut = false;
|
||||
}
|
||||
String parentPath = parent.getPathname();
|
||||
String user = ClientUtil.getUserName();
|
||||
try {
|
||||
if (folderItem instanceof DatabaseItem) {
|
||||
DatabaseItem databaseItem = (DatabaseItem) folderItem;
|
||||
BufferFile bufferFile = databaseItem.open();
|
||||
try {
|
||||
versionedFolderItem = versionedFileSystem.createDatabase(parentPath, name,
|
||||
folderItem.getFileID(), bufferFile, comment,
|
||||
folderItem.getContentType(), false, monitor, user);
|
||||
}
|
||||
finally {
|
||||
bufferFile.dispose();
|
||||
}
|
||||
}
|
||||
else if (folderItem instanceof DataFileItem) {
|
||||
DataFileItem dataFileItem = (DataFileItem) folderItem;
|
||||
InputStream istream = dataFileItem.getInputStream();
|
||||
try {
|
||||
versionedFolderItem = versionedFileSystem.createDataFile(parentPath, name,
|
||||
istream, comment, folderItem.getContentType(), monitor);
|
||||
}
|
||||
finally {
|
||||
istream.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new AssertException("Unknown folder item type");
|
||||
}
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
throw new AssertException("Unexpected error", e);
|
||||
else if (inUseDomainObj != null && !keepCheckedOut) {
|
||||
keepCheckedOut = true;
|
||||
Msg.warn(this, "File currently open - must keep checked-out: " + name);
|
||||
}
|
||||
|
||||
oldDomainObj = getOpenedDomainObject();
|
||||
synchronized (fileSystem) {
|
||||
|
||||
if (keepCheckedOut) {
|
||||
boolean exclusive = !versionedFileSystem.isShared();
|
||||
ProjectLocator projectLocator = parent.getProjectLocator();
|
||||
CheckoutType checkoutType;
|
||||
if (projectLocator.isTransient()) {
|
||||
checkoutType = CheckoutType.TRANSIENT;
|
||||
exclusive = true;
|
||||
String parentPath = parent.getPathname();
|
||||
String user = ClientUtil.getUserName();
|
||||
try {
|
||||
if (folderItem instanceof DatabaseItem) {
|
||||
DatabaseItem databaseItem = (DatabaseItem) folderItem;
|
||||
BufferFile bufferFile = databaseItem.open();
|
||||
try {
|
||||
versionedFolderItem = versionedFileSystem.createDatabase(parentPath,
|
||||
name, folderItem.getFileID(), bufferFile, comment,
|
||||
folderItem.getContentType(), false, monitor, user);
|
||||
}
|
||||
finally {
|
||||
bufferFile.dispose();
|
||||
}
|
||||
}
|
||||
else if (folderItem instanceof DataFileItem) {
|
||||
DataFileItem dataFileItem = (DataFileItem) folderItem;
|
||||
InputStream istream = dataFileItem.getInputStream();
|
||||
try {
|
||||
versionedFolderItem = versionedFileSystem.createDataFile(parentPath,
|
||||
name, istream, comment, folderItem.getContentType(), monitor);
|
||||
}
|
||||
finally {
|
||||
istream.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new AssertException("Unknown folder item type");
|
||||
}
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
throw new AssertException("Unexpected error", e);
|
||||
}
|
||||
|
||||
if (keepCheckedOut) {
|
||||
|
||||
// Maintain exclusive chekout if private repository or file is open for update
|
||||
boolean exclusive = !versionedFileSystem.isShared() || (inUseDomainObj != null);
|
||||
|
||||
ProjectLocator projectLocator = parent.getProjectLocator();
|
||||
CheckoutType checkoutType;
|
||||
if (projectLocator.isTransient()) {
|
||||
checkoutType = CheckoutType.TRANSIENT;
|
||||
exclusive = true;
|
||||
}
|
||||
else {
|
||||
// All checkouts for non-shared versioning are treated as exclusive
|
||||
checkoutType =
|
||||
(exclusive || !versionedFileSystem.isShared()) ? CheckoutType.EXCLUSIVE
|
||||
: CheckoutType.NORMAL;
|
||||
}
|
||||
ItemCheckoutStatus checkout = versionedFolderItem.checkout(checkoutType, user,
|
||||
ItemCheckoutStatus.getProjectPath(projectLocator.toString(),
|
||||
projectLocator.isTransient()));
|
||||
folderItem.setCheckout(checkout.getCheckoutId(), exclusive,
|
||||
checkout.getCheckoutVersion(), folderItem.getCurrentVersion());
|
||||
}
|
||||
else {
|
||||
// All checkouts for non-shared versioning are treated as exclusive
|
||||
checkoutType =
|
||||
(exclusive || !versionedFileSystem.isShared()) ? CheckoutType.EXCLUSIVE
|
||||
: CheckoutType.NORMAL;
|
||||
}
|
||||
ItemCheckoutStatus checkout = versionedFolderItem.checkout(checkoutType, user,
|
||||
ItemCheckoutStatus.getProjectPath(projectLocator.toString(),
|
||||
projectLocator.isTransient()));
|
||||
folderItem.setCheckout(checkout.getCheckoutId(), exclusive,
|
||||
checkout.getCheckoutVersion(), folderItem.getCurrentVersion());
|
||||
}
|
||||
else {
|
||||
if (oldDomainObj == null) {
|
||||
// NOTE: file open read-only may prevent removal and result in hijack
|
||||
try {
|
||||
folderItem.delete(-1, ClientUtil.getUserName());
|
||||
folderItem = null;
|
||||
@ -1111,27 +1127,23 @@ public class GhidraFileData {
|
||||
// Ignore - should result in Hijacked file
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldDomainObj != null) {
|
||||
|
||||
// TODO: Develop way to re-use and re-init domain object instead of a switch-a-roo approach
|
||||
if (inUseDomainObj != null) {
|
||||
getContentHandler().resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
}
|
||||
} // end of synchronized block
|
||||
|
||||
projectData.clearDomainObject(getPathname());
|
||||
|
||||
oldDomainObj.setDomainFile(new DomainFileProxy("~" + name, oldDomainObj));
|
||||
oldDomainObj.setTemporary(true);
|
||||
if (inUseDomainObj != null) {
|
||||
inUseDomainObj.invalidate();
|
||||
}
|
||||
}
|
||||
if (oldDomainObj != null) {
|
||||
// Complete re-open of file
|
||||
DomainFile df = getDomainFile();
|
||||
listener.domainFileObjectClosed(df, oldDomainObj);
|
||||
listener.domainFileObjectReplaced(df, oldDomainObj);
|
||||
}
|
||||
if (!keepCheckedOut) {
|
||||
finally {
|
||||
unlockDomainObject(inUseDomainObj);
|
||||
busy.set(false);
|
||||
projectData.mergeEnded();
|
||||
parent.deleteLocalFolderIfEmpty();
|
||||
parent.fileChanged(name);
|
||||
}
|
||||
statusChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1258,7 +1270,8 @@ public class GhidraFileData {
|
||||
if (versionedFolderItem.getCurrentVersion() != folderItem.getCheckoutVersion()) {
|
||||
return false;
|
||||
}
|
||||
// TODO: assumes folderItem is local - should probably defer createNewVersion to folderItem if possible (requires refactor)
|
||||
// TODO: assumes folderItem is local - should probably defer createNewVersion
|
||||
// to folderItem if possible (requires refactor)
|
||||
srcFile = (LocalManagedBufferFile) ((DatabaseItem) folderItem).open();
|
||||
}
|
||||
|
||||
@ -1266,18 +1279,15 @@ public class GhidraFileData {
|
||||
if (checkinHandler.createKeepFile()) {
|
||||
DomainObject sourceObj = null;
|
||||
try {
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
sourceObj = ch.getImmutableObject(folderItem, this, DomainFile.DEFAULT_VERSION,
|
||||
-1, monitor);
|
||||
sourceObj = getContentHandler().getImmutableObject(folderItem, this,
|
||||
DomainFile.DEFAULT_VERSION, -1, monitor);
|
||||
createKeepFile(sourceObj, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
// ignore - unable to create keep file
|
||||
}
|
||||
finally {
|
||||
if (sourceObj != null) {
|
||||
sourceObj.release(this);
|
||||
}
|
||||
release(sourceObj);
|
||||
}
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
@ -1298,13 +1308,13 @@ public class GhidraFileData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that current user is the checkout user for this file
|
||||
* Verify checkout status and that current user is the checkout user for this file
|
||||
* @param operationName name of user case (e.g., checkin)
|
||||
* @throws IOException if server/repository will not permit current user to checkin,
|
||||
* or update checkout version of current file. (i.e., server login does not match
|
||||
* user name used at time of initial checkout)
|
||||
*/
|
||||
private void verifyRepoUser(String operationName) throws IOException {
|
||||
private void verifyCheckout(String operationName) throws IOException {
|
||||
if (versionedFileSystem instanceof LocalFileSystem) {
|
||||
return; // rely on local project ownership
|
||||
}
|
||||
@ -1327,15 +1337,13 @@ public class GhidraFileData {
|
||||
* Performs check in to associated repository. File must be checked-out
|
||||
* and modified since checkout.
|
||||
* @param checkinHandler provides user input data to complete checkin process.
|
||||
* @param okToUpgrade if true an upgrade will be performed if needed
|
||||
* @param monitor the TaskMonitor.
|
||||
* @throws IOException if an IO or access error occurs
|
||||
* @throws VersionException if unable to handle domain object version in versioned filesystem.
|
||||
* If okToUpgrade was false, check exception to see if it can be upgraded
|
||||
* sometime after doing a checkout.
|
||||
* We are unable to upgrade since this would only occur if checkout is not exclusive.
|
||||
* @throws CancelledException if task monitor cancelled operation
|
||||
*/
|
||||
void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
|
||||
if (!versionedFileSystem.isOnline()) {
|
||||
@ -1357,15 +1365,29 @@ public class GhidraFileData {
|
||||
if (!modifiedSinceCheckout()) {
|
||||
throw new IOException("File has not been modified since checkout");
|
||||
}
|
||||
verifyRepoUser("checkin");
|
||||
verifyCheckout("checkin");
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitor.DUMMY;
|
||||
}
|
||||
|
||||
if (busy.getAndSet(true)) {
|
||||
throw new FileInUseException(name + " is busy");
|
||||
}
|
||||
|
||||
DomainObjectAdapterDB inUseDomainObj = null;
|
||||
projectData.mergeStarted();
|
||||
try {
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
|
||||
inUseDomainObj = getAndLockInUseDomainObjectForMergeUpdate("checkin");
|
||||
|
||||
boolean keepCheckedOut = checkinHandler.keepCheckedOut();
|
||||
|
||||
if (inUseDomainObj != null && !keepCheckedOut) {
|
||||
keepCheckedOut = true;
|
||||
Msg.warn(this, "File currently open - must keep checked-out: " + name);
|
||||
}
|
||||
|
||||
boolean quickCheckin = ALWAYS_MERGE ? false : quickCheckin(checkinHandler, monitor);
|
||||
|
||||
if (!quickCheckin) {
|
||||
@ -1377,9 +1399,8 @@ public class GhidraFileData {
|
||||
|
||||
Msg.info(this, "Checkin with merge for " + name);
|
||||
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
DomainObjectAdapter checkinObj = ch.getDomainObject(versionedFolderItem, null,
|
||||
folderItem.getCheckoutId(), okToUpgrade, false, this, monitor);
|
||||
DomainObjectAdapter checkinObj = contentHandler.getDomainObject(versionedFolderItem,
|
||||
null, folderItem.getCheckoutId(), false, false, this, monitor);
|
||||
checkinObj.setDomainFile(new DomainFileProxy(name, getParent().getPathname(),
|
||||
checkinObj, versionedFolderItem.getCurrentVersion() + 1, fileID,
|
||||
parent.getProjectLocator()));
|
||||
@ -1390,15 +1411,15 @@ public class GhidraFileData {
|
||||
try {
|
||||
synchronized (fileSystem) {
|
||||
int coVer = folderItem.getCheckoutVersion();
|
||||
sourceObj = ch.getImmutableObject(folderItem, this,
|
||||
sourceObj = contentHandler.getImmutableObject(folderItem, this,
|
||||
DomainFile.DEFAULT_VERSION, -1, monitor);
|
||||
originalObj =
|
||||
ch.getImmutableObject(versionedFolderItem, this, coVer, -1, monitor);
|
||||
latestObj = ch.getImmutableObject(versionedFolderItem, this,
|
||||
originalObj = contentHandler.getImmutableObject(versionedFolderItem, this,
|
||||
coVer, -1, monitor);
|
||||
latestObj = contentHandler.getImmutableObject(versionedFolderItem, this,
|
||||
DomainFile.DEFAULT_VERSION, coVer, monitor);
|
||||
}
|
||||
DomainObjectMergeManager mergeMgr =
|
||||
ch.getMergeManager(checkinObj, sourceObj, originalObj, latestObj);
|
||||
DomainObjectMergeManager mergeMgr = contentHandler.getMergeManager(checkinObj,
|
||||
sourceObj, originalObj, latestObj);
|
||||
|
||||
if (!mergeMgr.merge(monitor)) {
|
||||
Msg.info(this, "Checkin with merge terminated for " + name);
|
||||
@ -1417,27 +1438,14 @@ public class GhidraFileData {
|
||||
}
|
||||
finally {
|
||||
checkinObj.release(this);
|
||||
if (sourceObj != null) {
|
||||
sourceObj.release(this);
|
||||
}
|
||||
if (originalObj != null) {
|
||||
originalObj.release(this);
|
||||
}
|
||||
if (latestObj != null) {
|
||||
latestObj.release(this);
|
||||
}
|
||||
release(sourceObj);
|
||||
release(originalObj);
|
||||
release(latestObj);
|
||||
}
|
||||
}
|
||||
|
||||
DomainObjectAdapter oldDomainObj = null;
|
||||
|
||||
FolderItem oldLocalItem = null;
|
||||
boolean keepCheckedOut = checkinHandler.keepCheckedOut();
|
||||
|
||||
synchronized (fileSystem) {
|
||||
|
||||
oldDomainObj = getOpenedDomainObject();
|
||||
|
||||
versionedFolderItem = versionedFileSystem.getItem(parent.getPathname(), name);
|
||||
if (versionedFolderItem == null) {
|
||||
throw new IOException("Checkin failed, versioned item not found");
|
||||
@ -1456,7 +1464,17 @@ public class GhidraFileData {
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
// Failed to update checkout for unknown reason
|
||||
try {
|
||||
if (inUseDomainObj != null) {
|
||||
// On error disassociate open domain object from this file
|
||||
projectData.clearDomainObject(getPathname());
|
||||
// An invalid version (-2) is specified to avoid file match
|
||||
inUseDomainObj.setDomainFile(new DomainFileProxy(name,
|
||||
parent.getPathname(), inUseDomainObj, -2, fileID,
|
||||
parent.getProjectLocator()));
|
||||
inUseDomainObj.setTemporary(true);
|
||||
}
|
||||
undoCheckout(false, true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -1466,53 +1484,71 @@ public class GhidraFileData {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (oldDomainObj != null) {
|
||||
oldLocalItem = folderItem;
|
||||
folderItem = null;
|
||||
}
|
||||
else {
|
||||
undoCheckout(false, true);
|
||||
}
|
||||
undoCheckout(false, true);
|
||||
}
|
||||
if (oldDomainObj != null) {
|
||||
|
||||
// TODO: Develop way to re-use and re-init domain object instead of a switch-a-roo approach
|
||||
|
||||
projectData.clearDomainObject(getPathname());
|
||||
|
||||
oldDomainObj.setDomainFile(new DomainFileProxy(name, parent.getPathname(),
|
||||
oldDomainObj, -2, fileID, parent.getProjectLocator())); // invalid version (-2) specified to avoid file match
|
||||
oldDomainObj.setTemporary(true);
|
||||
if (inUseDomainObj != null) {
|
||||
contentHandler.resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDomainObj != null) {
|
||||
// complete re-open of domain file
|
||||
DomainFile df = getDomainFile();
|
||||
listener.domainFileObjectClosed(df, oldDomainObj);
|
||||
listener.domainFileObjectReplaced(df, oldDomainObj);
|
||||
}
|
||||
} // end of synchronized block
|
||||
|
||||
if (oldLocalItem != null) {
|
||||
synchronized (fileSystem) {
|
||||
// Undo checkout of old item - this will fail on Windows if item is open
|
||||
long checkoutId = oldLocalItem.getCheckoutId();
|
||||
oldLocalItem.delete(-1, ClientUtil.getUserName());
|
||||
versionedFolderItem.terminateCheckout(checkoutId, true);
|
||||
}
|
||||
if (inUseDomainObj != null) {
|
||||
inUseDomainObj.invalidate();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
unlockDomainObject(inUseDomainObj);
|
||||
busy.set(false);
|
||||
try {
|
||||
parent.deleteLocalFolderIfEmpty();
|
||||
parent.fileChanged(name);
|
||||
projectData.mergeEnded();
|
||||
parent.deleteLocalFolderIfEmpty();
|
||||
parent.fileChanged(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void release(DomainObject domainObj) {
|
||||
if (domainObj != null) {
|
||||
domainObj.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void unlockDomainObject(DomainObjectAdapterDB lockedDomainObject) {
|
||||
try {
|
||||
if (lockedDomainObject != null) {
|
||||
lockedDomainObject.unlock();
|
||||
}
|
||||
finally {
|
||||
projectData.mergeEnded();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Unexpected " + getContentType() + " lock error: " + getName());
|
||||
}
|
||||
}
|
||||
|
||||
private DomainObjectAdapterDB getAndLockInUseDomainObjectForMergeUpdate(String operation)
|
||||
throws IOException {
|
||||
DomainObjectAdapterDB inUseDomainObj;
|
||||
synchronized (fileSystem) {
|
||||
DomainObjectAdapter domainObj = getOpenedDomainObject();
|
||||
if (domainObj == null) {
|
||||
return null;
|
||||
}
|
||||
// If we proceed with file in-use it must be instance of DomainObjectAdapterDB
|
||||
if (!(domainObj instanceof DomainObjectAdapterDB)) {
|
||||
throw new FileInUseException(name + " is in use");
|
||||
}
|
||||
inUseDomainObj = (DomainObjectAdapterDB) domainObj;
|
||||
if (inUseDomainObj.isChanged()) {
|
||||
throw new FileInUseException(name + " is in use w/ unsaved changes");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that existing domain object will support DB merge update and is can be locked
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
if (!contentHandler.canResetDBSourceFile() || !inUseDomainObj.lock(operation) ||
|
||||
inUseDomainObj.getDBHandle().hasUncommittedChanges()) {
|
||||
throw new FileInUseException(name + " is in use");
|
||||
}
|
||||
|
||||
return inUseDomainObj;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1619,7 +1655,7 @@ public class GhidraFileData {
|
||||
throw new IOException("File not checked out");
|
||||
}
|
||||
if (!doForce) {
|
||||
verifyRepoUser("undo-checkout");
|
||||
verifyCheckout("undo-checkout");
|
||||
long checkoutId = folderItem.getCheckoutId();
|
||||
versionedFolderItem.terminateCheckout(checkoutId, true);
|
||||
}
|
||||
@ -1666,7 +1702,7 @@ public class GhidraFileData {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createKeepFile(DomainObject oldDomainObj, TaskMonitor monitor) {
|
||||
private void createKeepFile(DomainObject sourceObj, TaskMonitor monitor) {
|
||||
String keepName = name + ".keep";
|
||||
try {
|
||||
GhidraFileData keepFileData = parent.getFileData(keepName, false);
|
||||
@ -1683,7 +1719,7 @@ public class GhidraFileData {
|
||||
}
|
||||
keepName = getKeepName();
|
||||
Msg.info(this, "Creating old version keep file: " + keepName);
|
||||
parent.createFile(keepName, oldDomainObj, monitor);
|
||||
parent.createFile(keepName, sourceObj, monitor);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
throw new AssertException("Unexpected error", e);
|
||||
@ -1765,10 +1801,10 @@ public class GhidraFileData {
|
||||
|
||||
private void removeAssociatedUserDataFile() {
|
||||
try {
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
if (ch instanceof DBWithUserDataContentHandler) {
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
if (contentHandler instanceof DBWithUserDataContentHandler) {
|
||||
FolderItem item = folderItem != null ? folderItem : versionedFolderItem;
|
||||
((DBWithUserDataContentHandler<?>) ch).removeUserDataFile(item,
|
||||
((DBWithUserDataContentHandler<?>) contentHandler).removeUserDataFile(item,
|
||||
parent.getUserFileSystem());
|
||||
}
|
||||
}
|
||||
@ -1812,7 +1848,7 @@ public class GhidraFileData {
|
||||
if (canRecover()) {
|
||||
throw new IOException("File recovery data exists");
|
||||
}
|
||||
verifyRepoUser("merge");
|
||||
verifyCheckout("merge");
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitor.DUMMY;
|
||||
}
|
||||
@ -1821,8 +1857,11 @@ public class GhidraFileData {
|
||||
}
|
||||
|
||||
FolderItem tmpItem = null;
|
||||
DomainObjectAdapterDB inUseDomainObj = null;
|
||||
projectData.mergeStarted();
|
||||
try {
|
||||
inUseDomainObj = getAndLockInUseDomainObjectForMergeUpdate("merge");
|
||||
|
||||
if (!modifiedSinceCheckout()) {
|
||||
// Quick merge
|
||||
folderItem.updateCheckout(versionedFolderItem, true, monitor);
|
||||
@ -1830,17 +1869,17 @@ public class GhidraFileData {
|
||||
else {
|
||||
|
||||
if (SystemUtilities.isInHeadlessMode()) {
|
||||
throw new IOException(
|
||||
"Merge failed, file merge is not supported in headless mode");
|
||||
throw new IOException("Merge failed, merge is not supported in headless mode");
|
||||
}
|
||||
|
||||
ContentHandler<?> ch = getContentHandler();
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
|
||||
// Test versioned file for VersionException
|
||||
int mergeVer = versionedFolderItem.getCurrentVersion();
|
||||
if (!okToUpgrade) {
|
||||
DomainObject testObj =
|
||||
ch.getReadOnlyObject(versionedFolderItem, mergeVer, false, this, monitor);
|
||||
// verify remote version can be opened without verion error
|
||||
DomainObject testObj = contentHandler.getReadOnlyObject(versionedFolderItem,
|
||||
mergeVer, false, this, monitor);
|
||||
testObj.release(this);
|
||||
}
|
||||
|
||||
@ -1866,21 +1905,21 @@ public class GhidraFileData {
|
||||
|
||||
tmpItem.setCheckout(checkoutId, folderItem.isCheckedOutExclusive(), mergeVer, 0);
|
||||
|
||||
DomainObject mergeObj =
|
||||
ch.getDomainObject(tmpItem, null, -1, okToUpgrade, false, this, monitor);
|
||||
DomainObject mergeObj = contentHandler.getDomainObject(tmpItem, null, -1,
|
||||
okToUpgrade, false, this, monitor);
|
||||
DomainObject sourceObj = null;
|
||||
DomainObject originalObj = null;
|
||||
DomainObject latestObj = null; // TODO: Is there some way to leverage the buffer file we already copied into tmpItem? Missing required change set
|
||||
try {
|
||||
sourceObj = ch.getImmutableObject(folderItem, this, DomainFile.DEFAULT_VERSION,
|
||||
-1, monitor);
|
||||
originalObj =
|
||||
ch.getImmutableObject(versionedFolderItem, this, coVer, -1, monitor);
|
||||
latestObj =
|
||||
ch.getImmutableObject(versionedFolderItem, this, mergeVer, coVer, monitor);
|
||||
sourceObj = contentHandler.getImmutableObject(folderItem, this,
|
||||
DomainFile.DEFAULT_VERSION, -1, monitor);
|
||||
originalObj = contentHandler.getImmutableObject(versionedFolderItem, this,
|
||||
coVer, -1, monitor);
|
||||
latestObj = contentHandler.getImmutableObject(versionedFolderItem, this,
|
||||
mergeVer, coVer, monitor);
|
||||
|
||||
DomainObjectMergeManager mergeMgr =
|
||||
ch.getMergeManager(mergeObj, sourceObj, originalObj, latestObj);
|
||||
contentHandler.getMergeManager(mergeObj, sourceObj, originalObj, latestObj);
|
||||
|
||||
if (!mergeMgr.merge(monitor)) {
|
||||
Msg.info(this, "Merge terminated for " + name);
|
||||
@ -1888,19 +1927,14 @@ public class GhidraFileData {
|
||||
}
|
||||
|
||||
mergeObj.save("Merge with version " + mergeVer, monitor);
|
||||
|
||||
createKeepFile(sourceObj, monitor);
|
||||
}
|
||||
finally {
|
||||
mergeObj.release(this);
|
||||
if (sourceObj != null) {
|
||||
sourceObj.release(this);
|
||||
}
|
||||
if (originalObj != null) {
|
||||
originalObj.release(this);
|
||||
}
|
||||
if (latestObj != null) {
|
||||
latestObj.release(this);
|
||||
}
|
||||
release(mergeObj);
|
||||
release(sourceObj);
|
||||
release(originalObj);
|
||||
release(latestObj);
|
||||
}
|
||||
|
||||
// Update folder item
|
||||
@ -1909,29 +1943,18 @@ public class GhidraFileData {
|
||||
ClientUtil.getUserName());
|
||||
tmpItem = null;
|
||||
Msg.info(this, "Merge completed for " + name);
|
||||
}
|
||||
|
||||
DomainObjectAdapter oldDomainObj = null;
|
||||
|
||||
// TODO: Develop way to re-use and re-init domain object instead of a switch-a-roo approach
|
||||
|
||||
synchronized (fileSystem) {
|
||||
oldDomainObj = getOpenedDomainObject();
|
||||
if (oldDomainObj != null) {
|
||||
projectData.clearDomainObject(getPathname());
|
||||
oldDomainObj.setDomainFile(new DomainFileProxy("~" + name, oldDomainObj));
|
||||
oldDomainObj.setTemporary(true);
|
||||
if (inUseDomainObj != null) {
|
||||
contentHandler.resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDomainObj != null) {
|
||||
// Complete re-open of file
|
||||
DomainFile df = getDomainFile();
|
||||
listener.domainFileObjectClosed(df, oldDomainObj);
|
||||
listener.domainFileObjectReplaced(df, oldDomainObj);
|
||||
if (inUseDomainObj != null) {
|
||||
inUseDomainObj.invalidate();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
unlockDomainObject(inUseDomainObj);
|
||||
busy.set(false);
|
||||
try {
|
||||
if (tmpItem != null) {
|
||||
@ -2281,19 +2304,22 @@ public class GhidraFileData {
|
||||
/**
|
||||
* Returns an ordered map containing the metadata stored within a specific {@link FolderItem}
|
||||
* database. The map contains key,value pairs and are ordered by their insertion order.
|
||||
* @param item folder item whose metadata should be read
|
||||
* @return a map containing the metadata that has been associated with the corresponding domain
|
||||
* object. Map will be empty for a non-database item.
|
||||
*/
|
||||
static Map<String, String> getMetadata(FolderItem item) {
|
||||
if (!(item instanceof DatabaseItem databaseItem)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
ManagedBufferFile bf = null;
|
||||
DBHandle dbh = null;
|
||||
GenericDomainObjectDB genericDomainObj = null;
|
||||
try {
|
||||
if (item instanceof DatabaseItem) {
|
||||
DatabaseItem databaseItem = (DatabaseItem) item;
|
||||
BufferFile bf = databaseItem.open();
|
||||
DBHandle dbh = new DBHandle(bf);
|
||||
genericDomainObj = new GenericDomainObjectDB(dbh);
|
||||
return genericDomainObj.getMetadata();
|
||||
}
|
||||
bf = databaseItem.open();
|
||||
dbh = new DBHandle(bf);
|
||||
genericDomainObj = new GenericDomainObjectDB(dbh);
|
||||
return genericDomainObj.getMetadata();
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// file has been deleted, just return an empty map.
|
||||
@ -2308,6 +2334,12 @@ public class GhidraFileData {
|
||||
if (genericDomainObj != null) {
|
||||
genericDomainObj.release();
|
||||
}
|
||||
if (dbh != null) {
|
||||
dbh.close();
|
||||
}
|
||||
if (bf != null) {
|
||||
bf.dispose();
|
||||
}
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ class LinkedGhidraFile implements LinkedDomainFile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
public void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -96,8 +96,7 @@ public class ProjectDataTablePanel extends JPanel {
|
||||
}
|
||||
});
|
||||
gTable.getSelectionModel()
|
||||
.addListSelectionListener(
|
||||
e -> plugin.getTool().contextChanged(null));
|
||||
.addListSelectionListener(e -> plugin.getTool().contextChanged(null));
|
||||
gTable.setDefaultRenderer(Date.class, new DateCellRenderer());
|
||||
gTable.setDefaultRenderer(DomainFileType.class, new TypeCellRenderer());
|
||||
|
||||
@ -364,11 +363,6 @@ public class ProjectDataTablePanel extends JPanel {
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFolderSetActive(DomainFolder folder) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
if (ignoreChanges()) {
|
||||
@ -379,24 +373,6 @@ public class ProjectDataTablePanel extends JPanel {
|
||||
plugin.getTool().contextChanged(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
if (ignoreChanges()) {
|
||||
return;
|
||||
}
|
||||
clearInfo(file);
|
||||
table.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,11 +140,6 @@ class ChangeManager implements DomainFolderChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void domainFileSaved(DomainFile file, DomainObject dobj) {
|
||||
// treePanel.getActionManager().adjustActions();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
DomainFileNode fileNode = findDomainFileNode(file, true);
|
||||
@ -152,7 +147,6 @@ class ChangeManager implements DomainFolderChangeListener {
|
||||
fileNode.refresh();
|
||||
}
|
||||
treePanel.domainChange();
|
||||
// treePanel.getActionManager().adjustActions();
|
||||
}
|
||||
|
||||
private void getFolderPath(DomainFolder df, List<String> list) {
|
||||
@ -221,27 +215,4 @@ class ChangeManager implements DomainFolderChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.model.DomainFolderChangeListener#domainFileObjectReplaced(ghidra.framework.model.DomainFile, ghidra.framework.model.DomainObject)
|
||||
*/
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.model.DomainFolderChangeListener#domainFileObjectOpenedForUpdate(ghidra.framework.model.DomainFile, ghidra.framework.model.DomainObject)
|
||||
*/
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.model.DomainFolderChangeListener#domainFileObjectClosed(ghidra.framework.model.DomainFile, ghidra.framework.model.DomainObject)
|
||||
*/
|
||||
@Override
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,9 @@ public class CheckInTask extends VersionControlTask implements CheckinHandler {
|
||||
private void promptUser() throws CancelledException {
|
||||
if (newFile) {
|
||||
newFile = false;
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
if (actionID != VersionControlDialog.APPLY_TO_ALL) {
|
||||
showDialog(false, df.getName(), df.isLinkFile()); // false==> checking in vs.
|
||||
showDialog(false, df);
|
||||
// adding to version control
|
||||
if (actionID == VersionControlDialog.CANCEL) {
|
||||
monitor.cancel();
|
||||
@ -69,23 +67,16 @@ public class CheckInTask extends VersionControlTask implements CheckinHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor myMonitor) {
|
||||
this.monitor = myMonitor;
|
||||
myMonitor.setMessage("Examining selected file(s)");
|
||||
// checkFilesInUse();
|
||||
|
||||
String currentName = null;
|
||||
String currentContentType = null;
|
||||
try {
|
||||
for (int i = 0; i < list.size() && actionID != VersionControlDialog.CANCEL; i++) {
|
||||
|
||||
df = list.get(i);
|
||||
currentName = df.getName();
|
||||
currentContentType = df.getContentType();
|
||||
newFile = true;
|
||||
|
||||
if (i != 0) {
|
||||
@ -94,27 +85,23 @@ public class CheckInTask extends VersionControlTask implements CheckinHandler {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
catch (InterruptedException e2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
myMonitor.setMessage("Initiating Check In for " + currentName);
|
||||
try {
|
||||
df.checkin(this, false, myMonitor);
|
||||
df.checkin(this, myMonitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (VersionExceptionHandler.isUpgradeOK(parent, df, "Checkin", e)) {
|
||||
df.checkin(this, true, myMonitor);
|
||||
}
|
||||
VersionExceptionHandler.showVersionError(parent, df.getName(),
|
||||
df.getContentType(), "Checkin", e);
|
||||
}
|
||||
if (myMonitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(parent, df.getName(), currentContentType,
|
||||
"Checkin", e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
Msg.info(this, "Check In Process was canceled");
|
||||
wasCanceled = true;
|
||||
@ -125,18 +112,12 @@ public class CheckInTask extends VersionControlTask implements CheckinHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.data.CheckinHandler#getComment()
|
||||
*/
|
||||
@Override
|
||||
public String getComment() throws CancelledException {
|
||||
promptUser();
|
||||
return comments;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.data.CheckinHandler#keepCheckedOut()
|
||||
*/
|
||||
@Override
|
||||
public boolean keepCheckedOut() throws CancelledException {
|
||||
promptUser();
|
||||
|
@ -57,21 +57,22 @@ public abstract class VersionControlTask extends Task {
|
||||
* Show the dialog.
|
||||
* @param addToVersionControl true if the dialog is for
|
||||
* adding files to version control, false for checking in files.
|
||||
* @param filename the name of the file currently to be added, whose comment we need.
|
||||
* @param isLinkFile true if file is a link file, else false. Link-files may not be checked-out
|
||||
* so keep-checked-out control disabled if this is true.
|
||||
* @param file the file currently to be added or checked-in to version control
|
||||
*/
|
||||
protected void showDialog(boolean addToVersionControl, String filename, boolean isLinkFile) {
|
||||
protected void showDialog(boolean addToVersionControl, DomainFile file) {
|
||||
Runnable r = () -> {
|
||||
VersionControlDialog vcDialog = new VersionControlDialog(addToVersionControl);
|
||||
vcDialog.setCurrentFileName(filename);
|
||||
vcDialog.setCurrentFileName(file.getName());
|
||||
vcDialog.setMultiFiles(list.size() > 1);
|
||||
if (isLinkFile) {
|
||||
vcDialog.setKeepCheckboxEnabled(false, false, "Link files may not be Checked Out");
|
||||
if (file.isLinkFile()) {
|
||||
vcDialog.setKeepCheckboxEnabled(false, false, "Link file may not be Checked Out");
|
||||
}
|
||||
else if (filesInUse) {
|
||||
vcDialog.setKeepCheckboxEnabled(false, true,
|
||||
"Must keep Checked Out because the file is in use");
|
||||
else {
|
||||
checkFilesInUse();
|
||||
if (filesInUse) {
|
||||
vcDialog.setKeepCheckboxEnabled(false, true,
|
||||
"Must keep Checked Out because the file is in use");
|
||||
}
|
||||
}
|
||||
actionID = vcDialog.showDialog(tool, parent);
|
||||
keepCheckedOut = vcDialog.keepCheckedOut();
|
||||
@ -93,9 +94,11 @@ public abstract class VersionControlTask extends Task {
|
||||
* are still in use.
|
||||
*/
|
||||
protected void checkFilesInUse() {
|
||||
// NOTE: In-use check is currently limited to files open for update but for the purpose of
|
||||
// maintaining a checkout should really correspond to any file use (e.g., open read-only
|
||||
// with DomainFileProxy).
|
||||
filesInUse = false;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DomainFile df = list.get(i);
|
||||
for (DomainFile df : list) {
|
||||
if (df.getConsumers().size() > 0) {
|
||||
filesInUse = true;
|
||||
return;
|
||||
@ -104,8 +107,7 @@ public abstract class VersionControlTask extends Task {
|
||||
}
|
||||
|
||||
protected boolean checkFilesForUnsavedChanges() {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DomainFile df = list.get(i);
|
||||
for (DomainFile df : list) {
|
||||
if (df.modifiedSinceCheckout()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -136,14 +136,13 @@ public class VersionControlAddAction extends VersionControlAction {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
checkFilesInUse();
|
||||
try {
|
||||
for (DomainFile df : list) {
|
||||
String name = df.getName();
|
||||
monitor.setMessage("Adding " + name + " to Version Control");
|
||||
|
||||
if (actionID != VersionControlDialog.APPLY_TO_ALL) {
|
||||
showDialog(true, name, df.isLinkFile());
|
||||
showDialog(true, df);
|
||||
}
|
||||
if (actionID == VersionControlDialog.CANCEL) {
|
||||
return;
|
||||
|
@ -305,19 +305,34 @@ public interface DomainFile extends Comparable<DomainFile> {
|
||||
|
||||
/**
|
||||
* Returns true if this file may be checked-in to the associated repository.
|
||||
* @return true if can check-in
|
||||
*
|
||||
* Note: this does not take into consideration cases where the file is currently
|
||||
* in-use which may cause a failure if a checkin is attempted.
|
||||
*
|
||||
* @return true if a check-in can be attempted (i.e., file is checked-out with changes),
|
||||
* else false
|
||||
*/
|
||||
public boolean canCheckin();
|
||||
|
||||
/**
|
||||
* Returns true if this file can be merged with the current versioned file.
|
||||
* @return true if can merge
|
||||
*
|
||||
* Note: this does not take into consideration cases where the file is currently
|
||||
* in-use which may cause a failure if a merge is attempted.
|
||||
*
|
||||
* @return true if a merge can be attempted (i.e., file is checked-out and a newer
|
||||
* version exists), else false
|
||||
*/
|
||||
public boolean canMerge();
|
||||
|
||||
/**
|
||||
* Returns true if this private file may be added to the associated repository.
|
||||
* @return true if can add to the repository
|
||||
*
|
||||
* Note: this does not take into consideration cases where the file is currently
|
||||
* in-use which may cause a failure if add to repository is attempted.
|
||||
*
|
||||
* @return true if add to the repository can be attempted (i.e., file in active project
|
||||
* is not versioned or hijacked)
|
||||
*/
|
||||
public boolean canAddToRepository();
|
||||
|
||||
@ -381,7 +396,8 @@ public interface DomainFile extends Comparable<DomainFile> {
|
||||
/**
|
||||
* Adds this private file to version control.
|
||||
* @param comment new version comment
|
||||
* @param keepCheckedOut if true, the file will be initially checked-out
|
||||
* @param keepCheckedOut if true, the file will be initially checked-out. This option will be
|
||||
* ignored if file is currently open in which case file will remain checked-out.
|
||||
* @param monitor progress monitor
|
||||
* @throws FileInUseException if this file is in-use.
|
||||
* @throws IOException if an IO or access error occurs. Also if file is not
|
||||
@ -405,20 +421,42 @@ public interface DomainFile extends Comparable<DomainFile> {
|
||||
public boolean checkout(boolean exclusive, TaskMonitor monitor)
|
||||
throws IOException, CancelledException;
|
||||
|
||||
/**
|
||||
* Performs check in to associated repository. File must be checked-out
|
||||
* and modified since checkout.
|
||||
* @param checkinHandler provides user input data to complete checkin process.
|
||||
* The keep-checked-out option supplied by this handler will be ignored if file is currently
|
||||
* open in which case file will remain checked-out.
|
||||
* @param monitor the TaskMonitor.
|
||||
* @throws IOException if an IO or access error occurs
|
||||
* @throws VersionException if unable to handle domain object version in versioned filesystem.
|
||||
* We are unable to upgrade since this would only occur if checkout is not exclusive.
|
||||
* @throws CancelledException if task monitor cancelled operation
|
||||
*/
|
||||
public void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException;
|
||||
|
||||
/**
|
||||
* Performs check in to associated repository. File must be checked-out
|
||||
* and modified since checkout.
|
||||
* @param checkinHandler provides user input data to complete checkin process.
|
||||
* @param okToUpgrade if true an upgrade will be performed if needed
|
||||
* This keep-checked-out option supplied by this handler will be ignored and forced true
|
||||
* if file is currently open.
|
||||
* @param okToUpgrade if true an upgrade will be performed if needed (ignored)
|
||||
* @param monitor the TaskMonitor.
|
||||
* @throws IOException if an IO or access error occurs
|
||||
* @throws VersionException if unable to handle domain object version in versioned filesystem.
|
||||
* If okToUpgrade was false, check exception to see if it can be upgraded
|
||||
* sometime after doing a checkout.
|
||||
* @throws CancelledException if task monitor cancelled operation
|
||||
* @deprecated use alternative {@link #checkin(CheckinHandler, TaskMonitor)} method since
|
||||
* okToUpgrade cannot be respected and is ignored. Upgrade cannot be performed during checkin.
|
||||
*/
|
||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException;
|
||||
@Deprecated(since = "11.1", forRemoval = true)
|
||||
public default void checkin(CheckinHandler checkinHandler, boolean okToUpgrade,
|
||||
TaskMonitor monitor) throws IOException, VersionException, CancelledException {
|
||||
checkin(checkinHandler, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs merge from current version of versioned file into local checked-out file.
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* 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.
|
||||
@ -26,21 +25,27 @@ public interface DomainFolderChangeListener {
|
||||
* Notification that a folder is added to parent.
|
||||
* @param folder domain folder which was just added.
|
||||
*/
|
||||
public void domainFolderAdded(DomainFolder folder);
|
||||
public default void domainFolderAdded(DomainFolder folder) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a file is added to parent folder. You can
|
||||
* get the parent from the file.
|
||||
* @param file domain file which was just added.
|
||||
*/
|
||||
public void domainFileAdded(DomainFile file);
|
||||
public default void domainFileAdded(DomainFile file) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a domain folder is removed.
|
||||
* @param parent domain folder which contained the folder that was just removed.
|
||||
* @param name the name of the folder that was removed.
|
||||
*/
|
||||
public void domainFolderRemoved(DomainFolder parent, String name);
|
||||
public default void domainFolderRemoved(DomainFolder parent, String name) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a file was removed
|
||||
@ -48,40 +53,54 @@ public interface DomainFolderChangeListener {
|
||||
* @param name the name of the file that was removed.
|
||||
* @param fileID file ID or null
|
||||
*/
|
||||
public void domainFileRemoved(DomainFolder parent, String name, String fileID);
|
||||
public default void domainFileRemoved(DomainFolder parent, String name, String fileID) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners when a domain folder is renamed.
|
||||
* @param folder folder that was renamed
|
||||
* @param oldName old name of folder
|
||||
*/
|
||||
public void domainFolderRenamed(DomainFolder folder, String oldName);
|
||||
public default void domainFolderRenamed(DomainFolder folder, String oldName) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the domain file was renamed.
|
||||
* @param file file that was renamed
|
||||
* @param oldName old name of the file
|
||||
*/
|
||||
public void domainFileRenamed(DomainFile file, String oldName);
|
||||
public default void domainFileRenamed(DomainFile file, String oldName) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the domain folder was moved.
|
||||
* @param folder the folder (after move)
|
||||
* @param oldParent original parent folder
|
||||
*/
|
||||
public void domainFolderMoved(DomainFolder folder, DomainFolder oldParent);
|
||||
public default void domainFolderMoved(DomainFolder folder, DomainFolder oldParent) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the domain file was moved.
|
||||
* @param file the file (after move)
|
||||
* @param oldParent original parent folder
|
||||
* @param oldName file name prior to move
|
||||
*/
|
||||
public void domainFileMoved(DomainFile file, DomainFolder oldParent, String oldName);
|
||||
public default void domainFileMoved(DomainFile file, DomainFolder oldParent, String oldName) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the setActive() method on the folder was called.
|
||||
* @param folder folder which was activated/visited
|
||||
*/
|
||||
public void domainFolderSetActive(DomainFolder folder);
|
||||
public default void domainFolderSetActive(DomainFolder folder) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the status for a domain file has changed.
|
||||
@ -89,30 +108,25 @@ public interface DomainFolderChangeListener {
|
||||
* @param fileIDset if true indicates that the previously missing fileID has been
|
||||
* established for the specified file.
|
||||
*/
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset);
|
||||
|
||||
/**
|
||||
* Notification that a new version of the domain object exists and the
|
||||
* current one is no longer valid. Existing consumers should be immediately
|
||||
* released and no additional use of the oldObject is permitted once this
|
||||
* method returns. This is only called for domain objects which were
|
||||
* opened for update.
|
||||
* @param file file whose object was replaced
|
||||
* @param oldObject old object that was replaced
|
||||
*/
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject);
|
||||
public default void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a domain file has been opened for update.
|
||||
* @param file domain file
|
||||
* @param object domain object open for update
|
||||
*/
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object);
|
||||
public default void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a domain file previously open for update is in the process of closing.
|
||||
* @param file domain file
|
||||
* @param object domain object which was open for update
|
||||
*/
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object);
|
||||
public default void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -122,11 +122,6 @@ public abstract class DomainFolderListenerAdapter implements DomainFolderChangeL
|
||||
stateChanged(file.getPathname(), getPathname(oldParent, oldName), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFolderSetActive(DomainFolder folder) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
if (enableStateChangeCallback) {
|
||||
@ -135,18 +130,4 @@ public abstract class DomainFolderListenerAdapter implements DomainFolderChangeL
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainFileObjectClosed(DomainFile file, DomainObject object) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ public class TestDummyDomainFile implements DomainFile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||
public void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -21,14 +21,13 @@ import javax.swing.Icon;
|
||||
|
||||
import db.DBConstants;
|
||||
import db.DBHandle;
|
||||
import db.buffers.BufferFile;
|
||||
import db.buffers.ManagedBufferFile;
|
||||
import db.buffers.*;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.data.DBContentHandler;
|
||||
import ghidra.framework.data.DomainObjectMergeManager;
|
||||
import ghidra.framework.data.*;
|
||||
import ghidra.framework.model.ChangeSet;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.framework.store.local.LocalDatabaseItem;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -371,4 +370,21 @@ public class DataTypeArchiveContentHandler extends DBContentHandler<DataTypeArch
|
||||
return linkHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canResetDBSourceFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetDBSourceFile(FolderItem item, DomainObjectAdapterDB domainObj)
|
||||
throws IOException {
|
||||
if (!(item instanceof LocalDatabaseItem dbItem) ||
|
||||
!(domainObj instanceof DataTypeArchiveDB dataTypeArchive)) {
|
||||
throw new IllegalArgumentException("LocalDatabaseItem and DataTypeArchiveDB required");
|
||||
}
|
||||
LocalManagedBufferFile bf = dbItem.openForUpdate(FolderItem.DEFAULT_CHECKOUT_ID);
|
||||
dataTypeArchive.getDBHandle().setDBVersionedSourceFile(bf);
|
||||
getDataTypeArchiveChangeSet(dataTypeArchive, bf);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -509,9 +509,6 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.listing.Program#invalidate()
|
||||
*/
|
||||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
|
@ -20,14 +20,13 @@ import java.io.IOException;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import db.*;
|
||||
import db.buffers.BufferFile;
|
||||
import db.buffers.ManagedBufferFile;
|
||||
import db.buffers.*;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.data.DBWithUserDataContentHandler;
|
||||
import ghidra.framework.data.DomainObjectMergeManager;
|
||||
import ghidra.framework.data.*;
|
||||
import ghidra.framework.model.ChangeSet;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.framework.store.local.LocalDatabaseItem;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -58,8 +57,7 @@ public class ProgramContentHandler extends DBWithUserDataContentHandler<ProgramD
|
||||
if (!(obj instanceof ProgramDB)) {
|
||||
throw new IOException("Unsupported domain object: " + obj.getClass().getName());
|
||||
}
|
||||
return createFile((ProgramDB) obj, PROGRAM_CONTENT_TYPE, fs, path, name,
|
||||
monitor);
|
||||
return createFile((ProgramDB) obj, PROGRAM_CONTENT_TYPE, fs, path, name, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -363,4 +361,21 @@ public class ProgramContentHandler extends DBWithUserDataContentHandler<ProgramD
|
||||
return linkHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canResetDBSourceFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetDBSourceFile(FolderItem item, DomainObjectAdapterDB domainObj)
|
||||
throws IOException {
|
||||
if (!(item instanceof LocalDatabaseItem dbItem) ||
|
||||
!(domainObj instanceof ProgramDB program)) {
|
||||
throw new IllegalArgumentException("LocalDatabaseItem and ProgramDB required");
|
||||
}
|
||||
LocalManagedBufferFile bf = dbItem.openForUpdate(FolderItem.DEFAULT_CHECKOUT_ID);
|
||||
program.getDBHandle().setDBVersionedSourceFile(bf);
|
||||
getProgramChangeSet(program, bf);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1842,12 +1842,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangeable() {
|
||||
return changeable;
|
||||
|
@ -356,12 +356,6 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur
|
||||
*/
|
||||
public Address[] parseAddress(String addrStr, boolean caseSensitive);
|
||||
|
||||
/**
|
||||
* Invalidates any caching in a program.
|
||||
* NOTE: Over-using this method can adversely affect system performance.
|
||||
*/
|
||||
public void invalidate();
|
||||
|
||||
/**
|
||||
* Create a new overlay space based upon the given base AddressSpace
|
||||
* @param overlaySpaceName the name of the new overlay space.
|
||||
|
@ -536,11 +536,6 @@ public class StubProgram implements Program {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register getRegister(String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -126,13 +126,13 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator
|
||||
checkinComment = "Version 2";
|
||||
keepCheckedOut = true;
|
||||
assertTrue(df.canCheckin());
|
||||
df.checkin(this, false, null);
|
||||
df.checkin(this, null);
|
||||
|
||||
changeProgram(p, "bbb");
|
||||
checkinComment = "Version 3";
|
||||
keepCheckedOut = true;
|
||||
assertTrue(df.canCheckin());
|
||||
df.checkin(this, false, null);
|
||||
df.checkin(this, null);
|
||||
|
||||
p.release(this);
|
||||
performAction("Open File", "ProgramManagerPlugin", false);
|
||||
|
@ -216,21 +216,21 @@ public class VersionControlAction1Test extends AbstractVersionControlActionTest
|
||||
|
||||
// make some changes to check in
|
||||
Program program = (Program) ((DomainFileNode) node).getDomainFile()
|
||||
.getDomainObject(this,
|
||||
true, false, TaskMonitor.DUMMY);
|
||||
.getDomainObject(this, true, false, TaskMonitor.DUMMY);
|
||||
editProgram(program, (p) -> {
|
||||
SymbolTable symTable = p.getSymbolTable();
|
||||
symTable.createLabel(p.getMinAddress().getNewAddress(0x010001000), "fred",
|
||||
SourceType.USER_DEFINED);
|
||||
});
|
||||
program.release(this);
|
||||
|
||||
program = (Program) ((DomainFileNode) xnode).getDomainFile()
|
||||
.getDomainObject(this, true,
|
||||
false, TaskMonitor.DUMMY);
|
||||
.getDomainObject(this, true, false, TaskMonitor.DUMMY);
|
||||
editProgram(program, (p) -> {
|
||||
SymbolTable symTable = p.getSymbolTable();
|
||||
symTable.createLabel(p.getMinAddress(), "bob", SourceType.USER_DEFINED);
|
||||
});
|
||||
program.release(this);
|
||||
|
||||
DockingActionIf checkInAction = getAction("CheckIn");
|
||||
performAction(checkInAction, getDomainFileActionContext(node, xnode), false);
|
||||
@ -254,10 +254,8 @@ public class VersionControlAction1Test extends AbstractVersionControlActionTest
|
||||
|
||||
checkout(programNode);
|
||||
|
||||
Program program =
|
||||
(Program) ((DomainFileNode) programNode).getDomainFile()
|
||||
.getDomainObject(this, true,
|
||||
false, TaskMonitor.DUMMY);
|
||||
Program program = (Program) ((DomainFileNode) programNode).getDomainFile()
|
||||
.getDomainObject(this, true, false, TaskMonitor.DUMMY);
|
||||
|
||||
createHistoryEntry(program, "Symbol1");
|
||||
frontEnd.checkIn(programNode, "This is checkin 1");
|
||||
|
Loading…
Reference in New Issue
Block a user