blob: b3660855c616b7a994c0f22e65a11fb924ed94ba [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.util;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.team.*;
import org.eclipse.core.runtime.*;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
/**
* This hook exists to ensure that folders deletions will be recorded so that outgoing file
* deletions can be properly communicated to the server.
*/
public class MoveDeleteHook implements IMoveDeleteHook {
@Override
public boolean deleteFile(
final IResourceTree tree,
final IFile file,
final int updateFlags,
IProgressMonitor monitor) {
try {
monitor.beginTask(null, 100);
// No special handling required for team-private members
if (file.isTeamPrivateMember()) return false;
// If the file is ignored by CVS then we can just delete it.
ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file);
if (cvsFile.isIgnored()) return false;
// If we can't check out the files, return.
if (!checkOutFiles(tree, new IFile[] {file}, Policy.subMonitorFor(monitor, 30))) {
// Return that the delete was handled because the checkout
// will have reported the error to the IResourceTree
return true;
}
// Otherwise, we need to prepare properly for the delete
EclipseSynchronizer.getInstance().performMoveDelete(monitor1 -> {
try {
monitor1.beginTask(null, 100);
EclipseSynchronizer.getInstance().prepareForDeletion(file, Policy.subMonitorFor(monitor1, 40));
tree.standardDeleteFile(file, updateFlags, Policy.subMonitorFor(monitor1, 60));
} finally {
monitor1.done();
}
}, Policy.subMonitorFor(monitor, 70));
} catch (CVSException e) {
tree.failed(e.getStatus());
} finally {
monitor.done();
}
return true;
}
@Override
public boolean deleteFolder(
final IResourceTree tree,
final IFolder folder,
final int updateFlags,
IProgressMonitor monitor) {
// No special handling required for team-private members
if (folder.isTeamPrivateMember()) return false;
monitor.beginTask(null, 100);
try {
final ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(folder);
if (cvsFolder.isCVSFolder() && ensureCheckedOut(new IFolder[] {folder}, tree, Policy.subMonitorFor(monitor, 30))) {
EclipseSynchronizer.getInstance().performMoveDelete(monitor1 -> {
try {
monitor1.beginTask(null, 100);
EclipseSynchronizer.getInstance().prepareForDeletion(folder, Policy.subMonitorFor(monitor1, 20));
tree.standardDeleteFolder(folder, updateFlags, Policy.subMonitorFor(monitor1, 50));
} finally {
monitor1.done();
}
}, Policy.subMonitorFor(monitor, 70));
return true;
} else if (!cvsFolder.isIgnored()) {
EclipseSynchronizer.getInstance().prepareForDeletion(cvsFolder.getIResource(), Policy.subMonitorFor(monitor, 70));
}
} catch (CVSException e) {
tree.failed(e.getStatus());
} finally {
monitor.done();
}
return false;
}
@Override
public boolean deleteProject(
IResourceTree tree,
IProject project,
int updateFlags,
IProgressMonitor monitor) {
// We need to flush any remembered folder deletions for the deleted project.
// All other sync info is stored in session and persistant properties, which
// are deleted when the associated resources are deleted
try {
EclipseSynchronizer.getInstance().prepareForDeletion(project, monitor);
} catch (CVSException e) {
CVSProviderPlugin.log(e);
}
// todo: Perform a "cvs release" if there are any edits on the project
return false;
}
@Override
public boolean moveFile(
final IResourceTree tree,
final IFile source,
final IFile destination,
final int updateFlags,
IProgressMonitor monitor) {
try {
monitor.beginTask(null, 100);
// ensure we can write to both the source and the destination
IFile[] filesToCheckOut;
if (destination.exists()) {
filesToCheckOut = new IFile[] {source, destination};
} else {
filesToCheckOut = new IFile[] {source};
}
if (!checkOutFiles(tree, filesToCheckOut, Policy.subMonitorFor(monitor, 30))) {
// Return that the move was handled because the checkout
// will have reported the error to the IResourceTree
return true;
}
// Perform the move
EclipseSynchronizer.getInstance().performMoveDelete(monitor1 -> {
try {
monitor1.beginTask(null, 100);
EclipseSynchronizer.getInstance().prepareForDeletion(source, Policy.subMonitorFor(monitor1, 40));
if (destination.exists()) {
EclipseSynchronizer.getInstance().prepareForDeletion(destination, Policy.subMonitorFor(monitor1, 20));
}
tree.standardMoveFile(source, destination, updateFlags, Policy.subMonitorFor(monitor1, 40));
EclipseSynchronizer.getInstance().postMove(destination);
} finally {
monitor1.done();
}
}, Policy.subMonitorFor(monitor, 70));
} catch (CVSException e) {
tree.failed(e.getStatus());
} finally {
monitor.done();
}
return true;
}
@Override
public boolean moveFolder(
final IResourceTree tree,
final IFolder source,
final IFolder destination,
final int updateFlags,
IProgressMonitor monitor) {
monitor.beginTask(null, 100);
try {
final ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(source);
if (cvsFolder.isManaged()) {
if (!ensureCheckedOut(new IFolder[] {source, destination}, tree, Policy.subMonitorFor(monitor, 20))) return true;
EclipseSynchronizer.getInstance().performMoveDelete(new ICVSRunnable() {
public void run(IProgressMonitor monitor) throws CVSException {
EclipseSynchronizer.getInstance().prepareForDeletion(source, Policy.subMonitorFor(monitor, 20));
if (destination.exists()) {
EclipseSynchronizer.getInstance().prepareForDeletion(destination, Policy.subMonitorFor(monitor, 20));
}
tree.standardMoveFolder(source, destination, updateFlags, Policy.subMonitorFor(monitor, 30));
purgeCVSFolders(destination, Policy.subMonitorFor(monitor, 20));
EclipseSynchronizer.getInstance().postMove(destination);
}
private void purgeCVSFolders(IFolder destination, final IProgressMonitor monitor) throws CVSException {
// Delete any CVS folders
try {
destination.accept((IResourceVisitor) resource -> {
if (resource.getType() == IResource.FOLDER && resource.getName().equals(SyncFileWriter.CVS_DIRNAME)) {
tree.standardDeleteFolder((IFolder)resource, updateFlags, monitor);
return false;
}
return true;
}, IResource.DEPTH_INFINITE, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
} catch (CoreException e) {
throw CVSException.wrapException(e);
}
}
}, Policy.subMonitorFor(monitor, 60));
return true;
} else if (!cvsFolder.isIgnored()) {
EclipseSynchronizer.getInstance().prepareForDeletion(cvsFolder.getIResource(), Policy.subMonitorFor(monitor, 60));
}
} catch (CVSException e) {
tree.failed(e.getStatus());
return true;
} finally {
monitor.done();
}
return false;
}
@Override
public boolean moveProject(
IResourceTree tree,
IProject source,
IProjectDescription description,
int updateFlags,
IProgressMonitor monitor) {
// We need to move (or flush) any remembered folder deletions for the deleted project
// XXX We flush for now. This means that deleting a managed folder and then moving the
// project will mean that the file deletions will be lost. It also means that phantom
// folders are lost.
try {
EclipseSynchronizer.getInstance().prepareForDeletion(source, monitor);
} catch (CVSException e) {
CVSProviderPlugin.log(e);
}
return false;
}
/**
* Ensure that the given file is checked out (i.e. not read only). Return
* true if it is OK to procede and false otherwise.
*
* @param tree
* @param file
* @return boolean
*/
/* private */ boolean checkOutFiles(IResourceTree tree, IFile[] files, IProgressMonitor monitor) {
// Ensure that the file is "checked out" (i.e. not read-only
IFileModificationValidator validator = getFileModificationValidator(files);
if (validator instanceof ICVSFileModificationValidator) {
IStatus status = ((ICVSFileModificationValidator)validator).validateMoveDelete(files, monitor);
if (status.isOK()) {
return true;
} else {
tree.failed(status);
return false;
}
}
return true;
}
private boolean ensureCheckedOut(IFolder[] folders, IResourceTree tree, IProgressMonitor monitor) {
final List readOnlyFiles = new ArrayList();
try {
// Find any read-only files
for (IFolder folder : folders) {
if (folder.exists()) {
folder.accept(resource -> {
if (resource.getType() == IResource.FILE) {
IFile file = (IFile) resource;
if (file.isReadOnly()) {
readOnlyFiles.add(file);
}
}
return true;
});
}
}
if (readOnlyFiles.isEmpty()) return true;
// Ensure read-only files are checked out
return checkOutFiles(tree, (IFile[]) readOnlyFiles.toArray(new IFile[readOnlyFiles.size()]), monitor);
} catch (CoreException e) {
tree.failed(e.getStatus());
return false;
}
}
private FileModificationValidator getFileModificationValidator(IFile[] files) {
return getProvider(files).getFileModificationValidator2();
}
private CVSTeamProvider getProvider(IFile[] files) {
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(files[0].getProject(), CVSProviderPlugin.getTypeId());
return provider;
}
}