/*******************************************************************************
* Copyright (c) 2007, 2018 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.core.filebuffers.manipulation;
import java.util.ArrayList;
import org.eclipse.core.internal.filebuffers.FileBuffersPlugin;
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.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
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 GenericFileBufferOperationRunner
executes
* {@link org.eclipse.core.filebuffers.manipulation.IFileBufferOperation}.
* The runner takes care of all aspects that are not operation specific.
*
* This class is not intended to be subclassed. Clients instantiate this class. *
* * @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, ornull
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;
SubMonitor subMonitor= SubMonitor.convert(monitor, operation.getOperationName(), size * 200);
try {
IFileBuffer[] fileBuffers= createFileBuffers(locations, subMonitor.split(size * 10));
IFileBuffer[] fileBuffers2Save= findFileBuffersToSave(fileBuffers);
fFileBufferManager.validateState(fileBuffers2Save, subMonitor.split(size * 10), fValidationContext);
if (!isCommitable(fileBuffers2Save))
{
throw new OperationCanceledException();
}
IFileBuffer[] unsynchronizedFileBuffers= findUnsynchronizedFileBuffers(fileBuffers);
performOperation(unsynchronizedFileBuffers, operation, subMonitor.split(size * 40));
final IFileBuffer[] synchronizedFileBuffers= findSynchronizedFileBuffers(fileBuffers);
fIsCompleted= false;
fThrowable= null;
synchronized (fCompletionLock) {
executeInContext(() -> {
synchronized (fCompletionLock) {
try {
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable throwable) {
fThrowable= throwable;
}
@Override
public void run() throws Exception {
performOperation(synchronizedFileBuffers, operation, subMonitor.split(50));
}
});
} 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));
}
commit(fileBuffers2Save, subMonitor.split(size * 80));
} finally {
releaseFileBuffers(locations, subMonitor.split(size * 10));
}
}
private void performOperation(IFileBuffer fileBuffer, IFileBufferOperation operation, IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException {
SubMonitor subMonitor= SubMonitor.convert(progressMonitor, 100);
ISchedulingRule rule= fileBuffer.computeCommitRule();
IJobManager manager= Job.getJobManager();
manager.beginRule(rule, subMonitor.split(1));
String name= fileBuffer.getLocation().lastSegment();
subMonitor.setTaskName(name);
operation.run(fileBuffer, subMonitor.split(99));
manager.endRule(rule);
}
private void performOperation(IFileBuffer[] fileBuffers, IFileBufferOperation operation, IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException {
SubMonitor subMonitor= SubMonitor.convert(progressMonitor, fileBuffers.length);
for (IFileBuffer fileBuffer : fileBuffers) {
performOperation(fileBuffer, operation, subMonitor.split(1));
}
}
private void executeInContext(Runnable runnable) {
ITextFileBufferManager fileBufferManager= FileBuffers.getTextFileBufferManager();
fileBufferManager.execute(runnable);
}
private IFileBuffer[] findUnsynchronizedFileBuffers(IFileBuffer[] fileBuffers) {
ArrayList