mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02: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
46 changed files with 695 additions and 852 deletions
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue