blob: d8990ca50a1209b495c00189b07877c041aad408 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
import static java.text.MessageFormat.format;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IFileSystem;
import org.eclipse.tcf.services.IFileSystem.DoneRemove;
import org.eclipse.tcf.services.IFileSystem.DoneRename;
import org.eclipse.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IFSTreeNode;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.FSTreeNode;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.StatusHelper;
import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
/**
* FSMove moves specified tree nodes to a destination folder.
*/
public class OpMove extends AbstractOperation {
private static class WorkItem {
final WorkItem fParent;
final FSTreeNode fDestination;
final FSTreeNode fSource;
boolean fContentCleared = false;
boolean fContentLeftOK = false;
WorkItem(FSTreeNode source, FSTreeNode destination) {
this(null, source, destination);
}
WorkItem(WorkItem parent, FSTreeNode source, FSTreeNode destination) {
fParent = parent;
fSource = source;
fDestination = destination;
}
void setContentLeftOK() {
fContentLeftOK = true;
if (fParent != null)
fParent.setContentLeftOK();
}
}
IConfirmCallback fConfirmCallback;
LinkedList<WorkItem> fWork = new LinkedList<WorkItem>();
private long fStartTime;
public OpMove(List<? extends IFSTreeNode> nodes, FSTreeNode dest, IConfirmCallback confirmCallback) {
fConfirmCallback = confirmCallback;
for (FSTreeNode node : dropNestedNodes(nodes)) {
fWork.add(new WorkItem(node, dest));
}
}
@Override
public String getName() {
return Messages.OpMove_MovingFile;
}
@Override
public IStatus doRun(IProgressMonitor monitor) {
if (fWork.isEmpty())
return Status.OK_STATUS;
fStartTime = System.currentTimeMillis();
monitor.beginTask(getName(), IProgressMonitor.UNKNOWN);
List<FSTreeNode> notify = new ArrayList<FSTreeNode>();
notify.add(fWork.peek().fDestination);
for (WorkItem item : fWork) {
notify.add(item.fSource.getParent());
}
IStatus status = Status.OK_STATUS;
while (!fWork.isEmpty()) {
WorkItem item = fWork.remove();
status = runWorkItem(item, monitor);
if (!status.isOK())
break;
}
for (FSTreeNode node : dropNestedNodes(notify)) {
node.notifyChange();
}
return status;
}
protected IStatus runWorkItem(final WorkItem item, final IProgressMonitor monitor) {
if (item.fContentLeftOK)
return Status.OK_STATUS;
if (item.fContentCleared) {
return deleteEmptyFolder(item.fSource, monitor);
}
return move(item, monitor);
}
private IStatus move(final WorkItem item, final IProgressMonitor monitor) {
monitor.subTask(NLS.bind(Messages.OpMove_Moving, item.fSource.getLocation()));
final FSTreeNode source = item.fSource;
final FSTreeNode destination = item.fDestination;
IStatus status = refresh(destination, fStartTime, monitor);
if (!status.isOK())
return status;
status = refresh(source, fStartTime, monitor);
if (!status.isOK())
return status;
final FSTreeNode existing = destination.findChild(source.getName());
if (existing != null) {
if (source == existing) {
return Status.OK_STATUS;
}
if (source.isDirectory()) {
if (!existing.isDirectory()) {
return StatusHelper.createStatus(format(Messages.OpCopy_error_noDirectory, existing.getLocation()), null);
}
int replace = confirmCallback(existing, fConfirmCallback);
if (replace == IConfirmCallback.NO) {
item.setContentLeftOK();
return Status.OK_STATUS;
}
if (replace != IConfirmCallback.YES) {
return Status.CANCEL_STATUS;
}
item.fContentCleared = true;
fWork.addFirst(item);
for (FSTreeNode child : source.getChildren()) {
fWork.addFirst(new WorkItem(item, child, existing));
}
return Status.OK_STATUS;
} else if (source.isFile()) {
if (!existing.isFile()) {
return StatusHelper.createStatus(format(Messages.OpCopy_error_noFile, existing.getLocation()), null);
}
int replace = confirmCallback(existing, fConfirmCallback);
if (replace == IConfirmCallback.NO) {
item.setContentLeftOK();
return Status.OK_STATUS;
}
if (replace != IConfirmCallback.YES) {
return Status.CANCEL_STATUS;
}
} else {
return Status.OK_STATUS;
}
}
CacheManager.clearCache(existing);
CacheManager.clearCache(source);
final TCFResult<?> result = new TCFResult<Object>();
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
if (existing != null) {
tcfMoveReplace(source, destination, existing, result);
} else {
tcfMove(source, destination, result);
}
}
});
return result.waitDone(monitor);
}
protected void tcfMoveReplace(final FSTreeNode source, final FSTreeNode destination, final FSTreeNode existing, final TCFResult<?> result) {
if (result.checkCancelled())
return;
final IFileSystem fileSystem = destination.getRuntimeModel().getFileSystem();
if (fileSystem == null) {
result.setCancelled();
return;
}
fileSystem.remove(existing.getLocation(true), new DoneRemove() {
@Override
public void doneRemove(IToken token, FileSystemException error) {
if (error != null) {
result.setError(format(Messages.OpMove_CannotMove, source.getLocation()), error);
} else if (!result.checkCancelled()) {
existing.getParent().removeNode(existing, false);
tcfMove(source, destination, result);
}
}
});
}
protected void tcfMove(final FSTreeNode source, final FSTreeNode dest, final TCFResult<?> result) {
final IFileSystem fileSystem = dest.getRuntimeModel().getFileSystem();
if (fileSystem == null) {
result.setCancelled();
return;
}
final String sourcePath = source.getLocation(true);
final String destPath = getPath(dest, source.getName());
fileSystem.rename(sourcePath, destPath, new DoneRename() {
@Override
public void doneRename(IToken token, FileSystemException error) {
if (error != null) {
result.setError(format(Messages.OpMove_CannotMove, sourcePath), error);
} else {
source.getParent().removeNode(source, false);
source.changeParent(dest);
dest.addNode(source, false);
result.setDone(null);
}
}
});
}
private IStatus deleteEmptyFolder(final FSTreeNode source, IProgressMonitor monitor) {
CacheManager.clearCache(source);
final TCFResult<?> result = new TCFResult<Object>();
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
tcfDeleteEmptyFolder(source, result);
}
});
return result.waitDone(monitor);
}
protected void tcfDeleteEmptyFolder(final FSTreeNode source, final TCFResult<?> result) {
final IFileSystem fs = source.getRuntimeModel().getFileSystem();
if (fs == null) {
result.setCancelled();
return;
}
fs.rmdir(source.getLocation(true), new DoneRemove() {
@Override
public void doneRemove(IToken token, FileSystemException error) {
if (error != null) {
result.setError(format(Messages.OpDelete_error_delete, source.getLocation()), error);
} else if (!result.checkCancelled()) {
source.getParent().removeNode(source, false);
result.setDone(null);
}
}
});
}
}