| /******************************************************************************* |
| * 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); |
| } |