in source/com.microsoft.tfs.core/src/com/microsoft/tfs/core/clients/versioncontrol/engines/internal/GetEngine.java [1577:2442]
private void processOperation(final GetOperation action, final AsyncGetOperation asyncOp) {
Check.notNull(action, "action"); //$NON-NLS-1$
Check.notNull(asyncOp, "asyncOp"); //$NON-NLS-1$
// Get the new local item once since it has to be computed for
// GetOperations.
final String newLocalItem = action.getTargetLocalItem();
/*
* Check the path length here for compatibility with .NET, which
* discovers the condition when ItemSpec.GetFullPath() is used.
*
* Only do for non-preview, as the .NET implementation would only
* encounter the limit when writing files to disk.
*/
if (newLocalItem != null && !asyncOp.isPreview()) {
try {
LocalPath.checkLocalItem(newLocalItem, null, false, false, false, true);
} catch (final PathTooLongException e) {
log.warn("Path too long, not getting", e); //$NON-NLS-1$
onNonFatalError(
new VersionControlException(
MessageFormat.format(
Messages.getString("GetEngine.LocalPathTooLongFormat"), //$NON-NLS-1$
newLocalItem)),
asyncOp.getWorkspace());
return;
}
}
/*
* If this get operation has been overridden by the target conflict
* management code below (code that checks the existingLocalHash), then
* just ignore this. To better understand this see the code below where
* targetAction.ClearLocalItem() is called.
*/
if (action.isDownloadCompleted()) {
return;
}
// For conflicts, fire the conflict event.
if (action.hasConflict()) {
asyncOp.addConflict(action);
recordEvent(asyncOp, OperationStatus.CONFLICT, action);
return;
}
// Determine whether this is a pending rename that is changing the
// path's case.
final boolean isCaseChangingRename = action.isCaseChangingRename();
/*
* Tracks whether the operation's download is completed in this method,
* versus being queued for asynchronous processing, so we can set
* .tpattributes if it is complete.
*/
boolean downloadCompletedHere = false;
try {
// **************************************
// Error checks against the source item.
// **************************************
// Determine if the local item at the location we currently have it
// exists.
FileSystemAttributes existingLocalAttrs = new FileSystemAttributes();
boolean existingLocalExists = false;
if (action.getCurrentLocalItem() != null) {
existingLocalAttrs = FileSystemUtils.getInstance().getAttributes(action.getCurrentLocalItem());
existingLocalExists = existingLocalAttrs.exists();
log.debug(MessageFormat.format(
"existingLocalAttrs = {0}, existingLocalExists = {1}", //$NON-NLS-1$
existingLocalAttrs,
existingLocalExists));
/*
* If we are undoing an edit that is not also an add, set the
* file back to read-only so that we will not see it as a
* writable file later.
*/
if (asyncOp.getType() == ProcessType.UNDO) {
if (action.getChangeType().contains(ChangeType.EDIT)
&& action.getChangeType().contains(ChangeType.ADD) == false) {
action.setOkayToOverwriteExistingLocal(true);
if (WorkspaceLocation.SERVER == asyncOp.getWorkspace().getLocation()
&& existingLocalAttrs.isReadOnly() == false) {
existingLocalAttrs.setReadOnly(true);
existingLocalAttrs.setArchive(false);
FileSystemUtils.getInstance().setAttributes(
action.getCurrentLocalItem(),
existingLocalAttrs);
log.debug(
MessageFormat.format(
"Setting file to read only (archive=true) as part of undoing an edit: {0}", //$NON-NLS-1$
action.getCurrentLocalItem()));
}
}
}
// Check for problems deleting the source local file/directory.
if (existingLocalExists
&& (newLocalItem == null
|| LocalPath.equals(action.getCurrentLocalItem(), newLocalItem) == false)) {
// Check if we are getting a file but the source is actually
// a directory.
if (action.getItemType() == ItemType.FILE
&& !existingLocalAttrs.isSymbolicLink()
&& existingLocalAttrs.isDirectory()) {
// I have decided to not make this an error, but rather
// to skip the deletion of the source.
existingLocalExists = false;
}
}
}
// **************************************
// Error checks against the target item.
// **************************************
/*
* Check if there is a get operation against the target item. This
* code is critical for breaking rename cycles (the case where all
* items involved in the rename cycle have pending changes just
* results in a conflict for each -- otherwise, get should make
* progress).
*/
FileSystemAttributes newLocalAttrs = new FileSystemAttributes();
boolean newLocalExists = false;
GetOperation targetAction = null;
if (newLocalItem != null) {
newLocalAttrs = FileSystemUtils.getInstance().getAttributes(newLocalItem);
newLocalExists = newLocalAttrs.exists();
log.debug(MessageFormat.format(
"newLocalAttrs = {0}, newLocalExists = {1}", //$NON-NLS-1$
newLocalAttrs.toString(),
newLocalExists));
log.debug(MessageFormat.format("NewContentNeeded = {0}", action.isNewContentNeeded())); //$NON-NLS-1$
// Check if we are getting a file but the target is actually a
// directory.
if (newLocalExists
&& action.getItemType() != ItemType.FOLDER
&& !newLocalAttrs.isSymbolicLink()
&& newLocalAttrs.isDirectory()) {
asyncOp.addWarning(OperationStatus.TARGET_IS_DIRECTORY, action);
log.debug(MessageFormat.format("TargetIsDirectory, newLocalItem = {0}", newLocalItem)); //$NON-NLS-1$
return;
}
targetAction = asyncOp.getExistingLocalHash().get(newLocalItem);
if (targetAction != null && targetAction != action && !isCaseChangingRename) {
/*
* Check if there is a pending change against the target
* item. If there is a target pending change and we're not
* processing the results of a merge or unshelve or undo
* (e.g., unshelve cyclic rename), we'll stop processing the
* current action. None of these errors are possible if this
* is a case changing rename.
*/
if (newLocalExists
&& action.getType() != ProcessType.UNSHELVE
&& action.getType() != ProcessType.MERGE
&& action.getType() != ProcessType.ROLLBACK
&& action.getType() != ProcessType.UNDO
&& targetAction.getEffectiveChangeType().isEmpty() == false) {
asyncOp.addWarning(OperationStatus.TARGET_LOCAL_PENDING, action, targetAction);
log.debug(
MessageFormat.format(
"TargetLocalPending, newLocalItem = {0}, targetAction.ChangeType = {1}", //$NON-NLS-1$
newLocalItem,
targetAction.getEffectiveChangeType()));
return;
} else if (newLocalExists
&& !asyncOp.isOverwrite()
&& isWritableFileConflict(asyncOp, action, newLocalAttrs)
&& newLocalAttrs.isSymbolicLink() == false) {
// We have to stop if the target item is a writable
// file.
asyncOp.addWarning(OperationStatus.TARGET_WRITABLE, action);
log.debug(MessageFormat.format("TargetWritable, newLocalItem = {0}", newLocalItem)); //$NON-NLS-1$
return;
} else {
/*
* We have a get operation with this target as the
* source but it doesn't have a pending change. If the
* target is just a file, processing this action will
* handle it and there is no reason to complete the
* source portion of the target action. If the target is
* a folder, it will get added to the hash of items to
* not delete when the directory is created (see further
* down).
*/
if (targetAction.getItemType() == ItemType.FILE) {
/*
* In order to make this work in the face of
* crashes, I need to actually tell the server that
* I no longer have it. We must lock the target
* action across both clearing the local item and
* posting the update to prevent a race condition
* where the ULV call for a download could happen in
* between.
*/
synchronized (targetAction) {
log.debug(MessageFormat.format(
"ProcessOperation: clearing source local item {0}", //$NON-NLS-1$
action.getCurrentLocalItem()));
if (!asyncOp.isPreview() && !targetAction.isDownloadCompleted()) {
/*
* For a delete, we can complete the
* operation (for merge, we need to ack it
* as resolved as well). Otherwise, just
* tell the server we don't have the item.
*/
if (targetAction.isDelete()) {
asyncOp.queueLocalVersionUpdate(
targetAction,
null,
targetAction.getVersionLocal());
}
}
/*
* Only set the DownloadCompleted flag if the
* target action is a delete; othwerwise, the
* action hasn't been completed.
*/
if (targetAction.isDelete() && !targetAction.isDownloadCompleted()) {
targetAction.setDownloadCompleted(true);
downloadCompletedHere = true;
recordEvent(asyncOp, OperationStatus.DELETING, targetAction);
}
// We no longer have this item at this location
// --
// don't call until after using
// the local item path in the ULV call.
targetAction.clearLocalItem();
// Now remove the location from the hash.
asyncOp.getExistingLocalHash().remove(newLocalItem);
}
}
}
}
}
// if true, this is pending add which is being undone and we are
// asked to delete it afterwards
final boolean deleteAsUndoAdd = shouldDeleteAsUndoAdd(asyncOp, action);
// **************************************
// Time to perform the get.
// **************************************
// Check if we have something to get (rather than just deleting
// something).
if (!action.isDelete()) {
// Handle getting folders very differently from getting files.
if (action.getItemType() == ItemType.FOLDER) {
// Check if the target item is a writable file.
if (!asyncOp.isOverwrite()
&& newLocalExists
&& isWritableFileConflict(asyncOp, action, newLocalAttrs)) {
asyncOp.addWarning(OperationStatus.TARGET_WRITABLE, action);
log.debug(MessageFormat.format("TargetWritable, newLocalItem = {0}", newLocalItem)); //$NON-NLS-1$
return;
}
// Check if an item exists at the target location.
if (!asyncOp.isPreview() && !asyncOp.isNoDiskUpdate() && newLocalExists) {
// If it is just a file (we've already confirmed it's
// read-only) just delete it.
if (!newLocalAttrs.isDirectory()) {
if (!new File(newLocalItem).delete()) {
throw new IOException(
MessageFormat.format(
Messages.getString("GetEngine.CouldNotDeleteFileFormat"), //$NON-NLS-1$
newLocalItem));
} else {
log.debug(
MessageFormat.format(
"Deleting read-only file that''s in the way of a directory: {0}", //$NON-NLS-1$
newLocalItem));
}
newLocalExists = false;
}
}
String sourceLocalItem = null;
// if we are case changing rename and the item exists
// locally
if (isCaseChangingRename && existingLocalExists) {
sourceLocalItem = action.getCurrentLocalItem();
} else if (newLocalExists) {
// if the target already exists and we have a delete on
// the same path
// we convert it to a rename -- this takes care of the
// get /remap case
// where the case changes.
if (targetAction != null
&& targetAction.getItemType() == ItemType.FOLDER
&& targetAction.isDelete()
&& LocalPath.lastPartEqualsCaseSensitive(
targetAction.getCurrentLocalItem(),
newLocalItem) == false) {
sourceLocalItem = targetAction.getCurrentLocalItem();
}
}
// Create the directory. The call to create a directory does
// not throw an exception
// if the dir already exists (e.g., due to a race
// condition).
if (!asyncOp.isPreview() && (!newLocalExists || sourceLocalItem != null)) {
// If this is a case changing rename then we can safely
// do a Directory.Move, so we do,
// but only if source directory exists (Bug: 448888)
if (sourceLocalItem != null) {
try {
FileHelpers.rename(sourceLocalItem, newLocalItem);
log.debug(MessageFormat.format(
"Renamed directory: {0} -> {1}", //$NON-NLS-1$
action.getCurrentLocalItem(),
newLocalItem));
} catch (final IOException e) {
onNonFatalError(
new IOException(MessageFormat.format(
Messages.getString("GetEngine.FailedToRenameDirectoryFormat"), //$NON-NLS-1$
sourceLocalItem,
newLocalItem)),
asyncOp.getWorkspace());
}
} else {
if (!asyncOp.isNoDiskUpdate()) {
final File newLocalFile = new File(newLocalItem);
if (newLocalFile.mkdirs() == false) {
/*
* Double-check that the directory does not
* exist to avoid race conditions in mkdirs.
*/
if (!newLocalFile.isDirectory()) {
onNonFatalError(
new IOException(MessageFormat.format(
Messages.getString("GetEngine.FailedToCreateDirectoryFormat"), //$NON-NLS-1$
newLocalItem)),
asyncOp.getWorkspace());
}
} else {
log.debug(MessageFormat.format("Created directory: {0}", newLocalItem)); //$NON-NLS-1$
}
}
}
}
if (deleteAsUndoAdd) {
if (!asyncOp.getDeletes().containsKey(newLocalItem)) {
asyncOp.getDeletes().put(newLocalItem, action);
}
}
// Ensure that the newLocalItem folder will never be deleted
// by another operation.
else if (!asyncOp.getDontDeleteFolderHash().containsKey(newLocalItem)) {
asyncOp.getDontDeleteFolderHash().put(newLocalItem, action);
}
// Schedule the source file/directory for deletion if it is
// different from the target.
if (existingLocalExists && !LocalPath.equals(action.getCurrentLocalItem(), newLocalItem)) {
if (!asyncOp.getDeletes().containsKey(action.getCurrentLocalItem())) {
asyncOp.getDeletes().put(action.getCurrentLocalItem(), action);
}
// Go ahead and record the "move" and notify the server.
recordEvent(
asyncOp,
action.getCurrentLocalItem() == null ? OperationStatus.GETTING : OperationStatus.REPLACING,
action);
if (!asyncOp.isPreview()) {
asyncOp.queueLocalVersionUpdate(
action,
action.getTargetLocalItem(),
action.getVersionServer());
action.setDownloadCompleted(true);
downloadCompletedHere = true;
}
} else {
recordEvent(
asyncOp,
action.getCurrentLocalItem() == null ? OperationStatus.GETTING : OperationStatus.REPLACING,
action);
if (!asyncOp.isPreview()) {
if (asyncOp.getType() != ProcessType.PEND && asyncOp.getType() != ProcessType.UNDO
|| !action.getEffectiveChangeType().contains(ChangeType.ADD)) {
/*
* Queue a request to tell the server that I got
* it. In a local workspace, when getting a
* folder that we already have, use the force
* option.
*/
asyncOp.queueLocalVersionUpdate(
action,
action.getTargetLocalItem(),
action.getVersionServer(),
asyncOp.getWorkspace().getLocation() == WorkspaceLocation.LOCAL);
action.setDownloadCompleted(true);
downloadCompletedHere = true;
}
}
}
} else
// Getting a file.
{
/*
* If we are editing an existing file or the file exists at
* a different location, GetAll is false, and version local
* is the same as on the server, move the file.
*/
if (existingLocalExists
&& (action.getEffectiveChangeType().contains(ChangeType.EDIT)
&& action.getVersionLocal() == action.getVersionServer()
|| !asyncOp.isGetAll() && !action.isNewContentNeeded())) {
try {
// Force ignore case here so we can detect
// case-changing renames on all platforms.
if (LocalPath.equals(action.getCurrentLocalItem(), newLocalItem, true)) {
// When edit is true and we get to this point
// with the path not having
// changed, it is either a GetAll or there is
// nothing to download (the
// content didn't change on the server even
// though the version number did).
if (action.getEffectiveChangeType().contains(ChangeType.EDIT) && asyncOp.isGetAll()) {
// When GetAll is specified and the file is
// being edited, we obviously cannot
// download the file. Do NOT add it to the
// retry list!
recordEvent(asyncOp, OperationStatus.UNABLE_TO_REFRESH, action);
asyncOp.getStatus().incrementNumWarnings();
} else {
// If this isn't a preview and this is a case changing rename. (i.e. rename $/project -> $/PROJECT)
if (!asyncOp.isPreview() && isCaseChangingRename && !asyncOp.isNoDiskUpdate()) {
try {
FileHelpers.rename(action.getCurrentLocalItem(), newLocalItem);
log.debug(MessageFormat.format(
"Renamed file from {0} to {1}", //$NON-NLS-1$
action.getCurrentLocalItem(),
newLocalItem));
} catch (Exception e) {
onNonFatalError(
new IOException(MessageFormat.format(
Messages.getString("GetEngine.FailedToRenameFileFormat"), //$NON-NLS-1$
action.getCurrentLocalItem(),
newLocalItem)),
asyncOp.getWorkspace());
}
}
// For get there's nothing to do -- just go
// ahead and fire the event. For pend/undo
// the action happened on the server and we
// need to fire the event, though there
// is no disk update.
if (asyncOp.getType() != ProcessType.GET || isCaseChangingRename) {
recordEvent(asyncOp, OperationStatus.GETTING, action);
}
}
// There's nothing that needs to be downloaded.
if (!asyncOp.isPreview()) {
Check.isTrue(
!action.isNewContentNeeded()
|| (action.getEffectiveChangeType().contains(ChangeType.EDIT)
&& asyncOp.isGetAll()),
MessageFormat.format(
"Local and server versions expected to be equal except for edit: {0}", //$NON-NLS-1$
action));
if (deleteAsUndoAdd) {
if (new File(newLocalItem).delete() == false) {
throw new IOException(
MessageFormat.format(
Messages.getString("GetEngine.CouldNotDeleteFileFormat"), //$NON-NLS-1$
newLocalItem));
}
}
// No need to queue a local version update
// for the undo of a pending add. The call
// to UndoPendingChanges removes the local
// version row as part of that
// call.
else if (!(ProcessType.UNDO == asyncOp.getType()
&& action.getChangeType().contains(ChangeType.ADD))) {
asyncOp.queueLocalVersionUpdate(
action,
action.getTargetLocalItem(),
action.getVersionServer());
action.setDownloadCompleted(true);
downloadCompletedHere = true;
}
}
return;
}
} finally {
/*
* If the file is checked out, make sure it is
* writable. This is also necessary for the code
* further down that maintains the read-only setting
* of the source when performing the copy/delete
* move.
*/
if (!asyncOp.isPreview()
&& action.getEffectiveChangeType().contains(ChangeType.EDIT)
&& existingLocalAttrs.isReadOnly()
&& !existingLocalAttrs.isSymbolicLink()) {
existingLocalAttrs.setReadOnly(false);
existingLocalAttrs.setArchive(true);
FileSystemUtils.getInstance().setAttributes(
action.getCurrentLocalItem(),
existingLocalAttrs);
log.debug(MessageFormat.format(
"Set edited file to read/write (archive=true): {0}", //$NON-NLS-1$
action.getCurrentLocalItem()));
}
}
// Check for a writable target before attempting the
// move (we know it's not a directory).
if (!asyncOp.isOverwrite()
&& newLocalExists
&& isWritableFileConflict(asyncOp, action, newLocalAttrs)
&& !asyncOp.isNoDiskUpdate()) {
asyncOp.addWarning(OperationStatus.TARGET_WRITABLE, action);
return;
}
if (!asyncOp.isPreview() && !deleteAsUndoAdd && !asyncOp.isNoDiskUpdate()) {
/**
* We may get a rename for a file to a directory
* that does not exist. (Particularly when handling
* name conflicts and the user chooses the
* destination filename.)
*/
FileHelpers.createDirectoryIfNecessary(LocalPath.getParent(newLocalItem));
// Copy the source over the target file (we know at
// this point that the target
// must be read-only). If we are undoing pending add
// which is under pending rename, we just don't copy
// the file.
if (newLocalAttrs.exists()) {
new File(newLocalItem).delete();
}
if (!existingLocalAttrs.isSymbolicLink()) {
FileCopyHelper.copy(action.getCurrentLocalItem(), newLocalItem);
log.debug(MessageFormat.format(
"Copied file from {0} to {1}", //$NON-NLS-1$
action.getCurrentLocalItem(),
newLocalItem));
// Apply the appropriate last-write time
// forward.
if (asyncOp.getWorkspace().getOptions().contains(WorkspaceOptions.SET_FILE_TO_CHECKIN)
&& !DotNETDate.MIN_CALENDAR.equals(action.getVersionServerDate())) {
final File newLocalFile = new File(newLocalItem);
if (existingLocalAttrs.isReadOnly()) {
existingLocalAttrs.setReadOnly(false);
FileSystemUtils.getInstance().setAttributes(newLocalFile, existingLocalAttrs);
existingLocalAttrs.setReadOnly(true);
}
if (action.getEffectiveChangeType().contains(ChangeType.EDIT)) {
newLocalFile.setLastModified(action.getVersionServerDate().getTimeInMillis());
} else {
// Pending edit; carry the existing
// timestamp forward
final FileSystemAttributes attrs =
FileSystemUtils.getInstance().getAttributes(action.getCurrentLocalItem());
newLocalFile.setLastModified(attrs.getModificationTime().getJavaTime());
}
}
} else {
final FileSystemUtils util = FileSystemUtils.getInstance();
final String destinationPath = util.getSymbolicLink(action.getCurrentLocalItem());
util.createSymbolicLink(destinationPath, newLocalItem);
}
// We must preserve the read/write setting since the
// user may have attrib'ed the file outside of this program.
FileSystemUtils.getInstance().setAttributes(newLocalItem, existingLocalAttrs);
}
// Report that we are actually getting the file.
recordEvent(
asyncOp,
action.getCurrentLocalItem() == null ? OperationStatus.GETTING : OperationStatus.REPLACING,
action);
if (asyncOp.isPreview()) {
return;
}
// Tell the server that the file is in the new location.
asyncOp.queueLocalVersionUpdate(action, action.getTargetLocalItem(), action.getVersionServer());
synchronized (action) {
action.setDownloadCompleted(true);
downloadCompletedHere = true;
// Delete the source.
if (!asyncOp.isNoDiskUpdate()) {
if (new File(action.getCurrentLocalItem()).delete() == false) {
throw new IOException(
MessageFormat.format(
Messages.getString("GetEngine.CouldNotDeleteFileFormat"), //$NON-NLS-1$
action.getCurrentLocalItem()));
} else {
log.debug(MessageFormat.format(
"Deleted file source of move: {0}", //$NON-NLS-1$
action.getCurrentLocalItem()));
}
}
}
} else {
// Download the file unless the source or target is
// writable and Overwrite is not specified.
if (action.getEffectiveChangeType().contains(ChangeType.ADD) && !action.isNewContentNeeded()) {
// If the action is for a file with a pending add
// and we don't have or it is not on disk, there is
// nothing more we can do. For pend or undo, the
// change still happened on the server, so fire the
// regular event.
if (asyncOp.getType() == ProcessType.PEND || asyncOp.getType() == ProcessType.UNDO) {
recordEvent(asyncOp, OperationStatus.GETTING, action);
}
// Let the user know that there is an error unless
// we are processing an Undo request or a Pend that
// has Preview turned on (happens in the VSIP code).
if (!asyncOp.isNoDiskUpdate()
&& asyncOp.getType() != ProcessType.UNDO
&& (!asyncOp.isPreview() || asyncOp.getType() != ProcessType.PEND)) {
if (newLocalItem != null) {
onNonFatalError(
new VersionControlException(MessageFormat.format(
Messages.getString("GetEngine.AddedItemMissingLocallyFormat"), //$NON-NLS-1$
newLocalItem)),
asyncOp.getWorkspace());
} else {
onNonFatalError(
new VersionControlException(MessageFormat.format(
Messages.getString("GetEngine.AddedItemMissingLocallyFormat"), //$NON-NLS-1$
action.getCurrentLocalItem())),
asyncOp.getWorkspace());
}
}
return;
} else if (!asyncOp.isOverwrite()
&& WorkspaceLocation.SERVER == asyncOp.getWorkspace().getLocation()
&& existingLocalExists
&& !existingLocalAttrs.isReadOnly()
&& !action.isOkayToOverwriteExistingLocal()
&& !isCaseChangingRename
&& !localContentIsRedundant(action.getCurrentLocalItem(), action.getHashValue())
&& !existingLocalAttrs.isSymbolicLink()) {
asyncOp.addWarning(OperationStatus.SOURCE_WRITABLE, action, null);
} else if (!asyncOp.isOverwrite()
&& newLocalExists
&& isWritableFileConflict(asyncOp, action, newLocalAttrs)
&& !isCaseChangingRename
&& !newLocalAttrs.isSymbolicLink()) {
asyncOp.addWarning(OperationStatus.TARGET_WRITABLE, action);
return;
} else {
// Report that we are actually getting the file.
recordEvent(
asyncOp,
action.getCurrentLocalItem() == null ? OperationStatus.GETTING
: OperationStatus.REPLACING,
action);
if (action.isContentDestroyed()) {
onNonFatalError(
new DestroyedContentUnavailableException(MessageFormat.format(
Messages.getString("GetEngine.DestroyedFileContentUnavailableExceptionFormat"), //$NON-NLS-1$
action.getVersionServer(),
action.getTargetLocalItem())),
asyncOp.getWorkspace());
return;
}
// Don't go any further if we aren't actually
// getting it.
if (asyncOp.isPreview()) {
return;
}
Check.isTrue(
!deleteAsUndoAdd,
"We are downloading file which is not needed (undo of pending add)"); //$NON-NLS-1$
if (action.isUndo()
&& null != action.getBaselineFileGUID()
&& null == action.getDownloadURL()) {
// Local workspace offline undo (baseline folder
// restore)
if (!asyncOp.isNoDiskUpdate()) {
// check symbolic link first
final boolean isSymlink = PropertyConstants.IS_SYMLINK.equals(
PropertyUtils.selectMatching(
action.getPropertyValues(),
PropertyConstants.SYMBOLIC_KEY));
asyncOp.getBaselineFolders().copyBaselineToTarget(
action.getBaselineFileGUID(),
action.getTargetLocalItem(),
-1,
action.getHashValue(),
isSymlink);
}
if (asyncOp.getWorkspace().getOptions().contains(WorkspaceOptions.SET_FILE_TO_CHECKIN)
&& !DotNETDate.MIN_CALENDAR.equals(action.getVersionServerDate())) {
new File(action.getTargetLocalItem()).setLastModified(
action.getVersionServerDate().getTimeInMillis());
}
asyncOp.queueLocalVersionUpdate(
new ClientLocalVersionUpdate(
action.getSourceServerItem(),
action.getItemID(),
action.getTargetLocalItem(),
action.getVersionServer(),
action.getEncoding(),
false,
action.getPropertyValues()));
if (existingLocalExists
&& action.getCurrentLocalItem() != null
&& !LocalPath.equals(action.getCurrentLocalItem(), action.getTargetLocalItem())
&& !asyncOp.isNoDiskUpdate()) {
Check.isTrue(
action.getItemType() != ItemType.FOLDER,
MessageFormat.format(
"Should not try to delete a folder here: {0}", //$NON-NLS-1$
action.toString()));
deleteSource(action, existingLocalAttrs);
}
downloadCompletedHere = true;
} else if (null != action.getDownloadURL()) {
// Download URL get (common case)
asyncGetFile(
action,
existingLocalExists,
existingLocalAttrs,
newLocalExists,
newLocalAttrs,
asyncOp);
}
}
}
}
} else
// Operation is a delete.
{
// if the server sent back any and we want to do this if the
// existing item is a directory
if (action.getItemType() == ItemType.FOLDER
|| (action.getItemType() == ItemType.ANY
&& !existingLocalAttrs.isSymbolicLink()
&& existingLocalAttrs.isDirectory())) {
// Normally, we just have to queue folder deletes. This is
// because we don't want to do it until folders are empty
// and the async nature of get makes it really hard to
// determine when that is. When the current local path is
// null, we just fire an event.
if (action.getCurrentLocalItem() == null) {
recordEvent(asyncOp, OperationStatus.DELETING, action);
} else if (!asyncOp.getDeletes().containsKey(action.getCurrentLocalItem())) {
asyncOp.getDeletes().put(action.getCurrentLocalItem(), action);
}
} else {
// If the file is writable, stop. Otherwise, delete the
// file.
if (!asyncOp.isOverwrite()
&& existingLocalExists
&& WorkspaceLocation.SERVER == asyncOp.getWorkspace().getLocation()
&& !existingLocalAttrs.isReadOnly()
&& !existingLocalAttrs.isSymbolicLink()
&& !action.isOkayToOverwriteExistingLocal()) {
Check.isTrue(
!action.getEffectiveChangeType().contains(ChangeType.EDIT),
MessageFormat.format(
"The edit bit is set, yet we are trying to delete this file: {0}", //$NON-NLS-1$
action));
asyncOp.addWarning(OperationStatus.SOURCE_WRITABLE, action, null);
} else {
recordEvent(asyncOp, OperationStatus.DELETING, action);
// Don't go any further if we aren't actually deleting
// it.
if (asyncOp.isPreview()) {
return;
}
// Delete the file and acknowledge it.
deleteSource(action, existingLocalAttrs);
asyncOp.queueLocalVersionUpdate(
action,
null,
action.getVersionServer() != 0 ? action.getVersionServer() : action.getVersionLocal());
action.setDownloadCompleted(true);
downloadCompletedHere = true;
}
}
}
} catch (final PathTooLongException e) {
/*
* We already checked the target item at the top of this method, but
* LocalPath.canonicalize or LocalPath.checkLocalItem may have
* detected another path that exceeds the limit.
*/
log.warn("Path too long, not getting", e); //$NON-NLS-1$
onNonFatalError(
new VersionControlException(MessageFormat.format(
Messages.getString("GetEngine.LocalPathTooLongFormat"), //$NON-NLS-1$
newLocalItem)),
asyncOp.getWorkspace());
} catch (final CanceledException e) {
// Don't convert these to non-fatals
throw e;
} catch (final Exception e) {
// Note that we'll catch exceptions due to problems such as unable
// to open a file for writing because another process has it locked.
log.warn("Caught and converted an exception: ", e); //$NON-NLS-1$
onNonFatalError(e, asyncOp.getWorkspace());
} finally {
/*
* Apply .tpattributes if the download completed here. If it was
* queued for asynch processing, it will not be marked completed
* here and attributes get applied elsewhere.
*/
if (downloadCompletedHere && !asyncOp.isPreview() && !asyncOp.isNoDiskUpdate()) {
applyFileAttributesAfterGet(asyncOp, action);
}
}
}