blob: 0ac0a18fca9a4b6ae3b2903a7812ab08d50d7435 [file] [log] [blame]
package org.eclipse.wst.common.frameworks.internal.operations;
/*
* Licensed Material - Property of IBM (C) Copyright IBM Corp. 2001, 2002 - All
* Rights Reserved. US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.eclipse.core.internal.runtime.Assert;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.wst.common.frameworks.internal.WTPResourceHandler;
import org.eclipse.wst.common.frameworks.internal.enablement.nonui.WFTWrappedException;
import org.eclispe.wst.common.frameworks.internal.enablement.IEnablementIdentifier;
import org.eclispe.wst.common.frameworks.internal.enablement.IEnablementManager;
import org.eclispe.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
/**
* An operation which potentially makes changes to the workspace. All resource modification should
* be performed using this operation. The primary consequence of using this operation is that events
* which typically occur as a result of workspace changes (such as the firing of resource deltas,
* performance of autobuilds, etc.) are deferred until the outermost operation has successfully
* completed.
* <p>
* Subclasses must implement <code>execute</code> to do the work of the operation.
* </p>
*/
public abstract class WTPOperation implements IHeadlessRunnableWithProgress {
protected WTPOperationDataModel operationDataModel;
private OperationStatus opStatus;
private String id;
public WTPOperation(WTPOperationDataModel operationDataModel) {
setOperationDataModel(operationDataModel);
}
public WTPOperation() {
}
/**
* If the operationId is already set, setOperationId() will do nothing
*
* @param value
*/
public final void setID(String value) {
Assert.isTrue(this.id == null, WTPResourceHandler.getString("22")); //$NON-NLS-1$
Assert.isNotNull(value, WTPResourceHandler.getString("23")); //$NON-NLS-1$
this.id = value;
}
public final String getID() {
return this.id;
}
public final void setOperationDataModel(WTPOperationDataModel operationDataModel) {
this.operationDataModel = operationDataModel;
}
public final WTPOperationDataModel getOperationDataModel() {
return operationDataModel;
}
public IStatus getStatus() {
if (null == opStatus)
return WTPCommonPlugin.OK_STATUS;
return opStatus;
}
/**
* Performs the steps that are to be treated as a single logical workspace change.
* <p>
* Subclasses must implement this method.
* </p>
*
* @param monitor
* the progress monitor to use to display progress and field user requests to cancel
* @exception CoreException
* if the operation fails due to a CoreException
* @exception InvocationTargetException
* if the operation fails due to an exception other than CoreException
* @exception InterruptedException
* if the operation detects a request to cancel, using
* <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
* <code>InterruptedException</code>
*/
protected abstract void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException;
protected void initilize(IProgressMonitor monitor) {
//Making sure the status objects are initialized
// status = null;
opStatus = null;
}
private ComposedExtendedOperationHolder initializeExtensionOperations() {
return OperationExtensionRegistry.getExtensions(this);
}
protected void dispose(IProgressMonitor pm) {
}
protected IWorkspace getWorkspace() {
return ResourcesPlugin.getWorkspace();
}
public WTPOperation append(WTPOperation op) {
ComposedOperation composedOp = new ComposedOperation();
composedOp.addRunnable(this);
composedOp.addRunnable(op);
return composedOp;
}
/**
* Initiates a batch of changes, by invoking the execute() method as a workspace runnable.
*
* @param monitor
* the progress monitor to use to display progress
* @exception InvocationTargetException
* wraps any CoreException, runtime exception or error thrown by the execute()
* method
* @see WorkspaceModifyOperation - this class was directly copied from it
*/
public synchronized final void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
final InvocationTargetException[] iteHolder = new InvocationTargetException[1];
try {
IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor pm) throws CoreException {
try {
doRun(pm);
if (opStatus != null && !opStatus.isOK()) {
//TODO do something with the status
System.out.println(opStatus.getMessage());
}
} catch (InvocationTargetException e) {
// Pass it outside the workspace runnable
iteHolder[0] = e;
} catch (InterruptedException e) {
// Re-throw as OperationCanceledException, which will
// be
// caught and re-thrown as InterruptedException below.
throw new OperationCanceledException(e.getMessage());
}
}
};
ISchedulingRule rule = getSchedulingRule();
if (rule == null)
ResourcesPlugin.getWorkspace().run(workspaceRunnable, monitor);
else
ResourcesPlugin.getWorkspace().run(workspaceRunnable, rule, 0, monitor);
} catch (CoreException e) {
if (e.getStatus().getCode() == IResourceStatus.OPERATION_FAILED)
throw new WFTWrappedException(e.getStatus().getException(), e.getMessage());
throw new WFTWrappedException(e);
} catch (OperationCanceledException e) {
throw new InterruptedException(e.getMessage());
}
// Re-throw the InvocationTargetException, if any occurred
if (iteHolder[0] != null) {
throw new WFTWrappedException(iteHolder[0].getTargetException(), iteHolder[0].getMessage());
}
}
/**
* @return
*/
protected ISchedulingRule getSchedulingRule() {
return null;
}
public final void doRun(IProgressMonitor pm) throws CoreException, InvocationTargetException, InterruptedException {
boolean alreadyLocked = operationDataModel == null ? true : operationDataModel.isLocked();
boolean operationValidationEnabled = operationDataModel == null ? false : operationDataModel.isOperationValidationEnabled();
try {
if (!alreadyLocked) {
operationDataModel.setLocked(true);
}
if (operationValidationEnabled) {
operationDataModel.setOperationValidationEnabled(false);
IStatus status = operationDataModel.validateDataModel();
if (!status.isOK()) {
//TODO display something to user and remove System.out
System.out.println(WTPResourceHandler.getString("24", new Object[]{status.getMessage()})); //$NON-NLS-1$
Thread.dumpStack();
return;
}
}
initilize(pm);
if (!validateEdit())
return;
//if (getStatus().isOK()) {
ComposedExtendedOperationHolder extOpHolder = initializeExtensionOperations();
IStatus preOpStatus = runPreOps(pm, extOpHolder);
execute(pm);
IStatus postOpStatus = runPostOps(pm, extOpHolder);
if (null != preOpStatus)
addExtendedStatus(preOpStatus);
if (null != postOpStatus)
addExtendedStatus(postOpStatus);
//}
} finally {
dispose(pm);
if (!alreadyLocked) {
operationDataModel.setLocked(false);
}
if (operationValidationEnabled) {
operationDataModel.setOperationValidationEnabled(true);
}
}
// CoreException and OperationCanceledException are propagated
}
// Subclasses should override as they need to
protected boolean validateEdit() {
return true;
}
/**
* This is to track the status of the main flow of operations (extended operation stati are
* added through addExtendedStatus) execute() should call this method as necessary.
*
* @param aStatus
*/
protected final void addStatus(IStatus aStatus) {
if (opStatus == null) {
opStatus = new OperationStatus(aStatus.getMessage(), aStatus.getException());
opStatus.setSeverity(aStatus.getSeverity());
opStatus.add(aStatus);
} else {
opStatus.add(aStatus);
}
}
/**
* This is to keep track of extended operation stati. If the main status is WARNING, an extended
* status of ERROR will not make the main status ERROR. If the main status is OK, an extended
* status of ERROR or WARNING will make the main status WARNING.
*
* @param aStatus
*/
private void addExtendedStatus(IStatus aStatus) {
if (opStatus == null) {
opStatus = new OperationStatus(new IStatus[]{WTPCommonPlugin.OK_STATUS});
}
opStatus.addExtendedStatus(aStatus);
}
private IStatus runPostOps(IProgressMonitor pm, ComposedExtendedOperationHolder extOpHolder) {
IStatus postOpStatus = null;
if ((extOpHolder != null) && extOpHolder.hasPostOps()) {
postOpStatus = runExtendedOps(extOpHolder.getPostOps(), pm);
}
return postOpStatus;
}
private IStatus runPreOps(IProgressMonitor pm, ComposedExtendedOperationHolder extOpHolder) {
IStatus preOpStatus = null;
if ((extOpHolder != null) && extOpHolder.hasPreOps()) {
preOpStatus = runExtendedOps(extOpHolder.getPreOps(), pm);
}
return preOpStatus;
}
private IStatus runExtendedOps(List opList, IProgressMonitor pm) {
WTPOperation op = null;
OperationStatus returnStatus = null;
IStatus localStatus;
String opId = null;
IEnablementIdentifier enablementIdentifer = null;
for (int i = 0; i < opList.size(); i++) {
op = (WTPOperation) opList.get(i);
try {
opId = op.getID();
enablementIdentifer = IEnablementManager.INSTANCE.getIdentifier(opId, operationDataModel.getTargetProject());
if (enablementIdentifer.isEnabled()) {
op.setOperationDataModel(operationDataModel);
op.doRun(new SubProgressMonitor(pm, IProgressMonitor.UNKNOWN));
localStatus = op.getStatus();
} else
localStatus = null;
} catch (Exception e) {
localStatus = new Status(IStatus.ERROR, WTPCommonPlugin.PLUGIN_ID, 0, WTPResourceHandler.getString("25", new Object[]{op.getClass().getName()}), e); //$NON-NLS-1$
}
if (localStatus != null) {
if (returnStatus == null) {
returnStatus = new OperationStatus(new IStatus[]{localStatus});
} else {
returnStatus.add(localStatus);
}
}
}
return returnStatus;
}
// protected WTPOperation getRootExtendedOperation() {
// if (this.getRootExtendedOperation() == null)
// return this;
// return this.getRootExtendedOperation();
// }
protected static final void runNestedDefaultOperation(WTPOperationDataModel model, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
WTPOperation op = model.getDefaultOperation();
if (op != null)
op.run(monitor);
}
}