blob: df9343d6a88a30b3dec83aa1aa09d4611df9ec96 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 ALL4TEC & CEA LIST.
* 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:
* ALL4TEC & CEA LIST - initial API and implementation
******************************************************************************/
package org.polarsys.esf.core.common.ui.wizard;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.statushandlers.StatusManager;
import org.polarsys.esf.core.common.ui.CommonUIActivator;
import org.polarsys.esf.core.common.ui.job.FocusViewJob;
/**
* Generic wizard. It handles run of thread and logging, to ensure the same behaviour in all ESF wizards.
*
* TODO : add an adapt to ask to subclasses if they can send a project back to centralize the
* <code>SelectAndRevealResourceJob</code> call.
*
* @author $Author: jdumont $
* @version $Revision: 90 $
*
*/
public abstract class AbstractGenericWizard
extends Wizard {
/** ID for ESF info log. */
private static final String ID_SA_INFO_LOG = "org.polarsys.esf.view.log"; //$NON-NLS-1$
/** Title for job which focus on info log view after import. */
private static final String FOCUS_LOG_JOB_LABEL = CommonUIActivator.getMessages().getString(
"AbstractGenericWizard.refresh.workspace"); //$NON-NLS-1$
/** Constant used to indicate an unknown plugin id. */
private static final String UNKNOWN_PLUGIN_ID = "unknown"; //$NON-NLS-1$
/** Message show to user when finish is successful. */
private String mSuccessfulMessage = null;
/** Thread can be cancel (or not). */
private boolean mCancellable = false;
/**
* Default constructor.
*
* @param pSuccessfulMessage Message show to user when finish is successful.
* @param pCancellable true means that process launched will offer options to cancel it
*/
public AbstractGenericWizard(final String pSuccessfulMessage, final boolean pCancellable) {
super();
setNeedsProgressMonitor(true);
mSuccessfulMessage = pSuccessfulMessage;
mCancellable = pCancellable;
}
/**
* Hook offered to gather information from IHM before running action in non UI thread.
*
* Default implementation do nothing.
*/
protected void prepareFinish() {
}
/**
* {@inheritDoc}
*
* Do a synchronous call (in same thread than wizard) to prepareFinish, then run a IRunnable in a separate thread.
* This configuration need to get information from IHM in prepareFinish().
*/
@Override
public boolean performFinish() {
prepareFinish();
// Create job
final IRunnableWithProgress vRunnable = new IRunnableWithProgress() {
/**
* {@inheritDoc}
*/
@Override
public void run(final IProgressMonitor pMonitor) throws InvocationTargetException, InterruptedException {
// Perform the finish action and wrap the returned status
// to uniform the returns to display
IStatus vWrappedStatus = wrapStatus(doFinish(pMonitor));
// Extract the status type to display it correctly
int vHandleType = extractStatusType(vWrappedStatus);
StatusManager.getManager().handle(vWrappedStatus, vHandleType);
pMonitor.done();
// Show log view
showFinalView();
}
};
// Run job, with no cancel option
try {
getContainer().run(true, mCancellable, vRunnable);
} catch (final InvocationTargetException | InterruptedException pException) {
CommonUIActivator.logError(pException.getMessage(), pException);
}
// Always return true, as the wizard must not be kept opened even if something goes wrong
return true;
}
/**
* Wrap a given status to uniform how the returns are displayed
* in the log view. If no status is given, build a default OK status.
*
* @see AbstractGenericWizard#buildWrappedStatus(IStatus)
*
* @param pStatus A given status or null if no specific return is available
* @return The wrapped status
*/
protected IStatus wrapStatus(final IStatus pStatus) {
IStatus vWrappedStatus = null;
// Check the given status
if (pStatus == null) {
// No status is given, build a new default OK status
// to uniform the return in case of success
vWrappedStatus = new Status(Status.OK, CommonUIActivator.getPlugin().getSymbolicName(), mSuccessfulMessage);
} else {
// A status is given, thus extract its information if possible
// to build the wrapped status
vWrappedStatus = buildWrappedStatus(pStatus);
}
return vWrappedStatus;
}
/**
* Build a wrapped status from a one, to uniform how the returns are displayed
* in the log view. The wrapped status can manage the multi status.
*
* @param pStatus A given status, which must not be null
* @return The wrapped status built
*/
protected IStatus buildWrappedStatus(final IStatus pStatus) {
IStatus vWrappedStatus = null;
// Extract the status message, or take the 'successful' message for this
// action if the status is OK or Info or Warning
String vStatusMessage = null;
switch (pStatus.getSeverity()) {
case IStatus.OK:
case IStatus.INFO:
case IStatus.WARNING:
vStatusMessage = mSuccessfulMessage;
break;
default:
if (StringUtils.isNotEmpty(pStatus.getMessage())) {
vStatusMessage = pStatus.getMessage();
}
break;
}
// Extract the status plugin id, or take the generic
// plugin if its not given
String vStatusPlugin = CommonUIActivator.getPlugin().getSymbolicName();
if (StringUtils.isNotEmpty(pStatus.getPlugin()) && !UNKNOWN_PLUGIN_ID.equals(pStatus.getPlugin())) {
vStatusPlugin = pStatus.getPlugin();
}
if (pStatus.isMultiStatus()) {
// Build a new status to wrap the given multi status, and ensure
// to always display a uniform return
vWrappedStatus = new MultiStatus(vStatusPlugin, pStatus.getCode(), vStatusMessage, pStatus.getException());
// Merge the status children to keep all the status stack
// NB : This will set the multi-status severity according to the children
// maximum severity
((MultiStatus) vWrappedStatus).merge(pStatus);
} else {
// Build a new status to wrap the given single status, and ensure
// to always display uniform return
vWrappedStatus = new Status(pStatus.getSeverity(), vStatusPlugin, vStatusMessage);
}
return vWrappedStatus;
}
/**
* Extract the type of a status to know how it can be added
* in the status manager. By default it's always a 'LOG', and if
* the status is an error, add the 'BLOCK' type.
*
* @param pStatus The given status from which the type must be extracted, or null
* @return The status type found
*/
protected int extractStatusType(final IStatus pStatus) {
// By default, the status is a log
int vStatusType = StatusManager.LOG;
if ((pStatus != null) && pStatus.matches(Status.ERROR)) {
// If the status given is an error, add the Block type
vStatusType |= StatusManager.BLOCK;
}
return vStatusType;
}
/**
* @return The successfulMessage
*/
protected String getSuccessfulMessage() {
return mSuccessfulMessage;
}
/**
* @param pSuccessfulMessage The successfulMessage to set
*/
protected void setSuccessfulMessage(final String pSuccessfulMessage) {
mSuccessfulMessage = pSuccessfulMessage;
}
/**
* Show a specific view at the end of the wizard. By default, show log view.
*/
protected void showFinalView() {
FocusViewJob vJob = new FocusViewJob(FOCUS_LOG_JOB_LABEL, ID_SA_INFO_LOG);
vJob.schedule();
}
/**
* Do the concrete job for this wizard.
*
* Expected behaviour is that the subclass will return an IStatus with error if something goes wrong, or null
* otherwise.<br/>
* <br/>
*
* <b>Warning</b> : the <code>pMonitor.done()</code> should not be called in implementation, because performFinish()
* will do it after logging final message.<br/>
*
* <b>Warning</b> : the code in this method will be run in a non UI Thread. To get information from UI, do it before
* in {@link AbstractGenericWizard#prepareFinish()}.
* If an UI operation is done (like send focus to a specific view), call it in an UIJob.
*
* @param pMonitor Monitor used to follow job progression
* @return an exception if something goes wrong, or null otherwise
*/
protected abstract IStatus doFinish(IProgressMonitor pMonitor);
}