blob: b141a8849c8265d626c0e2df62a7d19b6679ed66 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.filebuffers.manipulation;
import java.util.ArrayList;
import org.eclipse.core.internal.filebuffers.FileBuffersPlugin;
import org.eclipse.core.internal.filebuffers.Progress;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.IFileBuffer;
import org.eclipse.core.filebuffers.IFileBufferManager;
import org.eclipse.core.filebuffers.IFileBufferStatusCodes;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
/**
* A <code>GenericFileBufferOperationRunner</code> executes
* {@link org.eclipse.core.filebuffers.manipulation.IFileBufferOperation}.
* The runner takes care of all aspects that are not operation specific.
* <p>
* This class is not intended to be subclassed. Clients instantiate this class.
* </p>
*
* @see org.eclipse.core.filebuffers.manipulation.IFileBufferOperation
* @since 3.3
* @noextend This class is not intended to be subclassed by clients.
*/
public class GenericFileBufferOperationRunner {
/** The validation context */
private final Object fValidationContext;
/** The file buffer manager */
private final IFileBufferManager fFileBufferManager;
/** The lock for waiting for completion of computation in the UI thread. */
private final Object fCompletionLock= new Object();
/** The flag indicating completion of computation in the UI thread. */
private transient boolean fIsCompleted;
/** The exception thrown during the computation in the UI thread. */
private transient Throwable fThrowable;
/**
* Creates a new file buffer operation runner.
*
* @param fileBufferManager the file buffer manager
* @param validationContext the validationContext
*/
public GenericFileBufferOperationRunner(IFileBufferManager fileBufferManager, Object validationContext) {
fFileBufferManager= fileBufferManager;
fValidationContext= validationContext;
}
/**
* Executes the given operation for all file buffers specified by the given locations.
*
* @param locations the file buffer locations
* @param operation the operation to be performed
* @param monitor the progress monitor, or <code>null</code> if progress reporting is not desired
* @throws CoreException in case of error
* @throws OperationCanceledException in case the execution get canceled
*/
public void execute(IPath[] locations, final IFileBufferOperation operation, IProgressMonitor monitor) throws CoreException, OperationCanceledException {
final int size= locations.length;
final IProgressMonitor progressMonitor= Progress.getMonitor(monitor);
progressMonitor.beginTask(operation.getOperationName(), size * 200);
try {
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, size * 10);
IFileBuffer[] fileBuffers= createFileBuffers(locations, subMonitor);
subMonitor.done();
IFileBuffer[] fileBuffers2Save= findFileBuffersToSave(fileBuffers);
subMonitor= Progress.getSubMonitor(progressMonitor, size * 10);
fFileBufferManager.validateState(fileBuffers2Save, subMonitor, fValidationContext);
subMonitor.done();
if (!isCommitable(fileBuffers2Save))
throw new OperationCanceledException();
IFileBuffer[] unsynchronizedFileBuffers= findUnsynchronizedFileBuffers(fileBuffers);
performOperation(unsynchronizedFileBuffers, operation, progressMonitor) ;
final IFileBuffer[] synchronizedFileBuffers= findSynchronizedFileBuffers(fileBuffers);
fIsCompleted= false;
fThrowable= null;
synchronized (fCompletionLock) {
executeInContext(new Runnable() {
@Override
public void run() {
synchronized(fCompletionLock) {
try {
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable throwable) {
fThrowable= throwable;
}
@Override
public void run() throws Exception {
performOperation(synchronizedFileBuffers, operation, progressMonitor);
}
});
} finally {
fIsCompleted= true;
fCompletionLock.notifyAll();
}
}
}
});
while (!fIsCompleted) {
try {
fCompletionLock.wait(500);
} catch (InterruptedException x) {
}
}
}
if (fThrowable != null) {
if (fThrowable instanceof CoreException)
throw (CoreException) fThrowable;
throw new CoreException(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IFileBufferStatusCodes.CONTENT_CHANGE_FAILED, fThrowable.getLocalizedMessage(), fThrowable));
}
subMonitor= Progress.getSubMonitor(progressMonitor, size * 80);
commit(fileBuffers2Save, subMonitor);
subMonitor.done();
} finally {
try {
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, size * 10);
releaseFileBuffers(locations, subMonitor);
subMonitor.done();
} finally {
progressMonitor.done();
}
}
}
private void performOperation(IFileBuffer fileBuffer, IFileBufferOperation operation, IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException {
ISchedulingRule rule= fileBuffer.computeCommitRule();
IJobManager manager= Job.getJobManager();
try {
manager.beginRule(rule, progressMonitor);
String name= fileBuffer.getLocation().lastSegment();
progressMonitor.beginTask(name, 100);
try {
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, 100);
operation.run(fileBuffer, subMonitor);
subMonitor.done();
} finally {
progressMonitor.done();
}
} finally {
manager.endRule(rule);
}
}
private void performOperation(IFileBuffer[] fileBuffers, IFileBufferOperation operation, IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException {
for (int i= 0; i < fileBuffers.length; i++) {
if (progressMonitor.isCanceled())
throw new OperationCanceledException();
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, 100);
performOperation(fileBuffers[i], operation, subMonitor);
subMonitor.done();
}
}
private void executeInContext(Runnable runnable) {
ITextFileBufferManager fileBufferManager= FileBuffers.getTextFileBufferManager();
fileBufferManager.execute(runnable);
}
private IFileBuffer[] findUnsynchronizedFileBuffers(IFileBuffer[] fileBuffers) {
ArrayList<IFileBuffer> list= new ArrayList<>();
for (int i= 0; i < fileBuffers.length; i++) {
if (!fileBuffers[i].isSynchronizationContextRequested())
list.add(fileBuffers[i]);
}
return list.toArray(new IFileBuffer[list.size()]);
}
private IFileBuffer[] findSynchronizedFileBuffers(IFileBuffer[] fileBuffers) {
ArrayList<IFileBuffer> list= new ArrayList<>();
for (int i= 0; i < fileBuffers.length; i++) {
if (fileBuffers[i].isSynchronizationContextRequested())
list.add(fileBuffers[i]);
}
return list.toArray(new IFileBuffer[list.size()]);
}
private IFileBuffer[] createFileBuffers(IPath[] locations, IProgressMonitor progressMonitor) throws CoreException {
progressMonitor.beginTask(FileBuffersMessages.FileBufferOperationRunner_task_connecting, locations.length);
try {
IFileBuffer[] fileBuffers= new ITextFileBuffer[locations.length];
for (int i= 0; i < locations.length; i++) {
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, 1);
fFileBufferManager.connect(locations[i], LocationKind.NORMALIZE, subMonitor);
subMonitor.done();
fileBuffers[i]= fFileBufferManager.getFileBuffer(locations[i], LocationKind.NORMALIZE);
}
return fileBuffers;
} catch (CoreException x) {
try {
releaseFileBuffers(locations, Progress.getMonitor());
} catch (CoreException e) {
}
throw x;
} finally {
progressMonitor.done();
}
}
private void releaseFileBuffers(IPath[] locations, IProgressMonitor progressMonitor) throws CoreException {
progressMonitor.beginTask(FileBuffersMessages.FileBufferOperationRunner_task_disconnecting, locations.length);
try {
final ITextFileBufferManager fileBufferManager= FileBuffers.getTextFileBufferManager();
for (int i= 0; i < locations.length; i++) {
IProgressMonitor subMonitor= Progress.getSubMonitor(progressMonitor, 1);
fileBufferManager.disconnect(locations[i], LocationKind.NORMALIZE, subMonitor);
subMonitor.done();
}
} finally {
progressMonitor.done();
}
}
private IFileBuffer[] findFileBuffersToSave(IFileBuffer[] fileBuffers) {
ArrayList<IFileBuffer> list= new ArrayList<>();
for (int i= 0; i < fileBuffers.length; i++) {
IFileBuffer buffer= fileBuffers[i];
if (!buffer.isDirty())
list.add(buffer);
}
return list.toArray(new IFileBuffer[list.size()]);
}
private boolean isCommitable(IFileBuffer[] fileBuffers) {
for (int i= 0; i < fileBuffers.length; i++) {
if (!fileBuffers[i].isCommitable())
return false;
}
return true;
}
protected ISchedulingRule computeCommitRule(IFileBuffer[] fileBuffers) {
ArrayList<ISchedulingRule> list= new ArrayList<>();
for (int i= 0; i < fileBuffers.length; i++) {
ISchedulingRule rule= fileBuffers[i].computeCommitRule();
if (rule != null)
list.add(rule);
}
ISchedulingRule[] rules= new ISchedulingRule[list.size()];
list.toArray(rules);
return new MultiRule(rules);
}
protected void commit(final IFileBuffer[] fileBuffers, final IProgressMonitor progressMonitor) throws CoreException {
IProgressMonitor monitor= Progress.getMonitor(progressMonitor);
ISchedulingRule rule= computeCommitRule(fileBuffers);
Job.getJobManager().beginRule(rule, progressMonitor);
try {
doCommit(fileBuffers, progressMonitor);
} finally {
Job.getJobManager().endRule(rule);
monitor.done();
}
}
protected void doCommit(final IFileBuffer[] fileBuffers, IProgressMonitor progressMonitor) throws CoreException {
IProgressMonitor monitor= Progress.getMonitor(progressMonitor);
monitor.beginTask(FileBuffersMessages.FileBufferOperationRunner_task_committing, fileBuffers.length);
try {
for (int i= 0; i < fileBuffers.length; i++) {
IProgressMonitor subMonitor= Progress.getSubMonitor(monitor, 1);
fileBuffers[i].commit(subMonitor, true);
subMonitor.done();
}
} finally {
monitor.done();
}
}
}