/*******************************************************************************
* Copyright (c) 2011, 2014 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.runtime.callback;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.Assert;
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.tcf.te.runtime.activator.CoreBundleActivator;
import org.eclipse.tcf.te.runtime.interfaces.IConditionTester;
import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
import org.eclipse.tcf.te.runtime.nls.Messages;
import org.eclipse.tcf.te.runtime.properties.PropertiesContainer;
import org.eclipse.tcf.te.runtime.utils.ProgressHelper;
import org.eclipse.tcf.te.runtime.utils.StatusHelper;
/**
* Default implementation of the <code>ICallback</code> interface.
*/
public class Callback extends PropertiesContainer implements ICallback {
protected static final String PROPERTY_PARENT_CALLBACK = "parentCallback"; //$NON-NLS-1$
protected static final String PROPERTY_PROGRESS_MONITOR = "progressMonitor"; //$NON-NLS-1$
protected static final String PROPERTY_PROGRESS_TICKS = "progressTicks"; //$NON-NLS-1$
protected static final String PROPERTY_IS_DONE = "isDone"; //$NON-NLS-1$
public static final String PROPERTY_STATUS = "status"; //$NON-NLS-1$
/**
* Property: Asynchronous operations can store a result to the callback
* object they invoke once the operation has been finished.
*/
protected static final String PROPERTY_RESULT = "result"; //$NON-NLS-1$
private static final String[] PROPERTY_KEYS_NOT_TO_COPY = {
PROPERTY_PARENT_CALLBACK, PROPERTY_PROGRESS_MONITOR,
PROPERTY_PROGRESS_TICKS, PROPERTY_IS_DONE, PROPERTY_STATUS
};
private static final List<String> PROPERTY_KEYS_NOT_TO_COPY_LIST = Arrays.asList(PROPERTY_KEYS_NOT_TO_COPY);
/**
* Condition tester for ExecutorsUtil to check whether the callback is done
* or the {@link IProgressMonitor} is canceled.
*/
public static class CallbackDoneConditionTester implements IConditionTester {
protected final ICallback callback;
protected final IProgressMonitor monitor;
protected int cancelTimeout = -1;
protected long cancelTime = -1;
/**
* Constructor.
*
* @param callback The callback to check. Must not be <code>null</code>.
* @param monitor The progress monitor to check or <code>null</code>.
*/
public CallbackDoneConditionTester(ICallback callback, IProgressMonitor monitor) {
this(callback, monitor, -1);
}
/**
* Constructor.
*
* @param callback The callback to check. Must not be <code>null</code>.
* @param monitor The progress monitor to check or <code>null</code>.
* @param cancelTimeout Timeout in milliseconds to wait for callback.isDone() when progress monitor was canceled.
*/
public CallbackDoneConditionTester(ICallback callback, IProgressMonitor monitor, int cancelTimeout) {
Assert.isNotNull(callback);
this.callback = callback;
this.monitor = monitor;
this.cancelTimeout = cancelTimeout;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.IConditionTester#isConditionFulfilled()
*/
@Override
public boolean isConditionFulfilled() {
if (monitor == null)
return callback.isDone();
// handle cancel timeout
if (cancelTimeout > 0 && !callback.isDone() && monitor.isCanceled()) {
if (cancelTime == -1) {
cancelTime = System.currentTimeMillis();
monitor.subTask("Cancelling..."); //$NON-NLS-1$
}
else {
long currentTime = System.currentTimeMillis();
if ((currentTime - cancelTime) >= cancelTimeout) {
callback.done(this, StatusHelper.getStatus(new OperationCanceledException(Messages.Callback_warning_cancelTimeout)));
return true;
}
}
}
return callback.isDone();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.IConditionTester#cleanup()
*/
@Override
public void cleanup() {
}
}
/**
* Constructor.
*/
public Callback() {
this(null);
}
/**
* Constructor to wrap a parent callback.
*
* @param parentCallback
* The parent callback.
*/
public Callback(ICallback parentCallback) {
this(null, -1, parentCallback);
}
/**
* Constructor to handle a progress monitor.
*
* @param monitor
* The progress monitor.
* @param ticksToUse
* The ticks to add when done.
*/
public Callback(IProgressMonitor monitor, int ticksToUse) {
this(monitor, ticksToUse, null);
}
/**
* Constructor to handle a progress monitor and wrap a parent callback.
*
* @param monitor
* The progress monitor.
* @param ticksToUse
* The ticks to add when done.
* @param parentCallback
* The parent callback.
*/
public Callback(IProgressMonitor monitor, int ticksToUse, ICallback parentCallback) {
super();
setProperty(PROPERTY_PARENT_CALLBACK, parentCallback);
setProperty(PROPERTY_PROGRESS_MONITOR, monitor);
setProperty(PROPERTY_PROGRESS_TICKS, ticksToUse);
}
/**
* Get a condition tester for this callback.
*
* @param monitor The progress monitor to check or <code>null</code>.
* @return The condition tester.
*/
public final IConditionTester getDoneConditionTester(IProgressMonitor monitor) {
return new CallbackDoneConditionTester(this, monitor);
}
/**
* Get a condition tester for this callback.
*
* @param monitor The progress monitor to check or <code>null</code>.
* @param cancelTimeout Timeout in milliseconds to wait for callback.isDone() when progress monitor was canceled.
* @return The condition tester.
*/
public final IConditionTester getDoneConditionTester(IProgressMonitor monitor, int cancelTimeout) {
return new CallbackDoneConditionTester(this, monitor, cancelTimeout);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.callback.ICallback#done(java.lang.Object, org.eclipse.core.runtime.IStatus)
*/
@Override
public final void done(Object caller, IStatus status) {
Assert.isNotNull(status);
if (isDone()) {
if (getStatus() != null && getStatus().getSeverity() != IStatus.CANCEL)
CoreBundleActivator.getTraceHandler().trace("WARNING: callback called twice!!", 1, this); //$NON-NLS-1$
return;
}
setProperty(PROPERTY_IS_DONE, null);
setProperty(PROPERTY_STATUS, checkStatusIntegrity(status));
internalDone(caller, (IStatus) getProperty(PROPERTY_STATUS));
if (getProperty(PROPERTY_IS_DONE) == null) {
setProperty(PROPERTY_IS_DONE, true);
}
if (isDone()) {
if (getProperty(PROPERTY_PROGRESS_MONITOR) instanceof IProgressMonitor) {
IProgressMonitor progress = ((IProgressMonitor) getProperty(PROPERTY_PROGRESS_MONITOR));
if (!ProgressHelper.isCanceled(progress) && getStatus().getSeverity() != IStatus.CANCEL) {
int ticks = getIntProperty(PROPERTY_PROGRESS_TICKS);
if (ticks > 0) {
ProgressHelper.worked(progress, ticks);
} else if (ticks == ProgressHelper.PROGRESS_DONE) {
ProgressHelper.done(progress);
}
}
}
ICallback parentCallback = (ICallback) getProperty(PROPERTY_PARENT_CALLBACK);
if (parentCallback != null) {
if (parentCallback.isDone()) {
CoreBundleActivator.getTraceHandler().trace("WARNING: parent callback called twice!!", 1, this); //$NON-NLS-1$
}
else {
copyProperties(this, parentCallback);
if (!ProgressHelper.isCancelOrError(this,
getStatus(),
(IProgressMonitor) getProperty(PROPERTY_PROGRESS_MONITOR),
parentCallback)) {
parentCallback.done(caller, getStatus());
}
}
}
}
}
/**
* Copy the properties from the given source callback to the given
* destination callback.
*
* @param source
* The source callback. Must not be <code>null</code>.
* @param destination
* The destination callback. Must not be <code>null</code> and
* not yet done.
*/
public static final void copyProperties(ICallback source, ICallback destination) {
Assert.isNotNull(source);
Assert.isNotNull(destination);
Assert.isTrue(!destination.isDone());
for (String key : source.getProperties().keySet()) {
if (!PROPERTY_KEYS_NOT_TO_COPY_LIST.contains(key)) {
destination.setProperty(key, source.getProperty(key));
}
}
Assert.isTrue(!destination.isDone());
}
/**
* Checks the status integrity.
*
* @param status
* The status or <code>null</code>.
* @return The checked status.
*/
private IStatus checkStatusIntegrity(IStatus status) {
if (status == null) status = Status.OK_STATUS;
if (status.getSeverity() == IStatus.CANCEL && status.getException() == null) {
status = new Status(IStatus.CANCEL, status.getPlugin(),
status.getMessage(), new OperationCanceledException(status.getMessage()));
}
if (status.getSeverity() == IStatus.WARNING && status.getException() == null) {
status = new Status(IStatus.WARNING, status.getPlugin(),
status.getMessage(), new Exception(status.getMessage()));
}
if (status.getSeverity() == IStatus.ERROR && status.getException() == null) {
status = new Status(IStatus.ERROR, status.getPlugin(),
status.getMessage(), new Exception(status.getMessage()));
}
return status;
}
/**
* Return the progress monitor or <code>null</code>.
*/
protected final IProgressMonitor getProgressMonitor() {
return (IProgressMonitor) getProperty(PROPERTY_PROGRESS_MONITOR);
}
/**
* Internal callback done.
*
* @param caller
* The caller.
* @param status
* The status.
*/
protected void internalDone(Object caller, IStatus status) {
}
/**
* Return the result on done.
*/
public final IStatus getStatus() {
IStatus status = (IStatus) getProperty(PROPERTY_STATUS);
if (status == null && getProperty(PROPERTY_PROGRESS_MONITOR) instanceof IProgressMonitor) {
IProgressMonitor monitor = (IProgressMonitor) getProperty(PROPERTY_PROGRESS_MONITOR);
if (monitor.isCanceled()) {
return checkStatusIntegrity(Status.CANCEL_STATUS);
}
}
return checkStatusIntegrity(status);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.callback.ICallback#isDone()
*/
@Override
public final boolean isDone() {
return getBooleanProperty(PROPERTY_IS_DONE);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.callback.ICallback#addParentCallback(org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
*/
@Override
public void addParentCallback(ICallback callback) {
if (getProperty(PROPERTY_PARENT_CALLBACK) instanceof ICallback) {
ICallback parentCallback = (ICallback) getProperty(PROPERTY_PARENT_CALLBACK);
parentCallback.addParentCallback(callback);
} else {
setProperty(PROPERTY_PARENT_CALLBACK, callback);
}
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.callback.ICallback#setResult(java.lang.Object)
*/
@Override
public void setResult(Object result) {
setProperty(PROPERTY_RESULT, result);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.interfaces.callback.ICallback#getResult()
*/
@Override
public Object getResult() {
return getProperty(PROPERTY_RESULT);
}
}