/******************************************************************************* * Copyright (c) 2011, 2016 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.ui.statushandler; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; import org.eclipse.tcf.te.runtime.statushandler.AbstractStatusHandler; import org.eclipse.tcf.te.runtime.statushandler.interfaces.IStatusHandlerConstants; import org.eclipse.tcf.te.runtime.utils.Host; import org.eclipse.tcf.te.runtime.utils.StatusHelper; import org.eclipse.tcf.te.ui.activator.UIPlugin; import org.eclipse.tcf.te.ui.jface.dialogs.OptionalMessageDialog; import org.eclipse.tcf.te.ui.nls.Messages; import org.eclipse.ui.PlatformUI; /** * The default status handler implementation. * <p> * This status handler is returned by the status handler manager if no other * status handler can be found for a given context object. */ public class DefaultStatusHandler extends AbstractStatusHandler { // Declare some default title messages protected final static String QUESTION_TITLE = Messages.DefaultStatusHandler_question_title; protected final static String WARNING_TITLE = Messages.DefaultStatusHandler_warning_title; protected final static String ERROR_TITLE = Messages.DefaultStatusHandler_error_title; protected final static String INFORMATION_TITLE = Messages.DefaultStatusHandler_information_title; /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.statushandler.interfaces.IStatusHandler#handleStatus(org.eclipse.core.runtime.IStatus, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback) */ @Override public void handleStatus(final IStatus status, final IPropertiesContainer data, final ICallback done) { Assert.isNotNull(status); // If the platform UI is not longer running or the display does not // exist or is disposed already, don't do anything. Display display = PlatformUI.getWorkbench().getDisplay(); if (!PlatformUI.isWorkbenchRunning() || display == null || display.isDisposed()) { return; } // The message dialog has to open within the display thread. Check if we are in // the correct display thread and spawn to it if not. if (Thread.currentThread().equals(display.getThread())) { // The current thread is the display thread, execute synchronously doHandleStatus(status, data, done); } else { // The current thread is not the display, execute asynchronously display.asyncExec(new Runnable() { @Override public void run() { doHandleStatus(status, data, done); } }); } } /** * Execute the status handling. * <p> * <b>Note:</b> This method must be called within the platforms display thread. * * @param status The status. Must not be <code>null</code>. * @param data The custom status data object, or <code>null</code> if none. * @param done The callback, or <code>null</code>. */ protected void doHandleStatus(IStatus status, IPropertiesContainer data, ICallback done) { Assert.isNotNull(status); Assert.isTrue(Thread.currentThread().equals(PlatformUI.getWorkbench().getDisplay().getThread())); Object result = null; Throwable error = null; try { // Unpack the status object String message = status.getMessage(); String pluginId = status.getPlugin(); int code = status.getCode(); int severity = status.getSeverity(); Throwable exception = status.getException(); String title = null; String[] buttonLabel = null; String contextHelpId = null; String dontAskAgainId = null; Object caller = null; String detailsText = null; int detailsButtonIndex = -1; // Unpack the custom data if (data != null) { title = data.getStringProperty(IStatusHandlerConstants.PROPERTY_TITLE); buttonLabel = (String[])data.getProperty(IStatusHandlerConstants.PROPERTY_BUTTON_LABEL); contextHelpId = data.getStringProperty(IStatusHandlerConstants.PROPERTY_CONTEXT_HELP_ID); dontAskAgainId = data.getStringProperty(IStatusHandlerConstants.PROPERTY_DONT_ASK_AGAIN_ID); caller = data.getProperty(IStatusHandlerConstants.PROPERTY_CALLER); detailsText = data.getStringProperty(IStatusHandlerConstants.PROPERTY_DETAILS_TEXT); detailsButtonIndex = data.getIntProperty(IStatusHandlerConstants.PROPERTY_DETAILS_BUTTON_INDEX); } if (message != null && pluginId != null) { // Determine the shell (null if workbench is not running, typically for headless mode). Shell shell = null; if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell() != null) { shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); } // Invoke subclass hook to overwrite the severity severity = adjustSeverity(severity, exception); // Invoke subclass hook to overwrite the title title = adjustTitle(title, severity, exception); // Invoke subclass hook to overwrite the message message = adjustMessage(message, severity, exception, caller); if (Host.isInteractive() && shell != null) { // we can show a real dialogs to the user. However, warnings and // errors will go to the error log too. It will give us a clue later // if we have to analyze the log files for possible failure scenarios. // Use a default dialog box title in case no specific title is given. if (title == null) { switch (severity) { case IStatusHandlerConstants.QUESTION: case IStatusHandlerConstants.YES_NO_CANCEL: title = QUESTION_TITLE; break; case IStatus.WARNING: title = WARNING_TITLE; break; case IStatus.ERROR: title = ERROR_TITLE; break; default: title = INFORMATION_TITLE; } } // In case the status represents a warning or an error, // log the status to the error log. if (severity == IStatus.WARNING || severity == IStatus.ERROR) { UIPlugin.getDefault().getLog().log(status); } // Invoke subclass hook to overwrite the context help id. String[] contextHelpIds = adjustContextHelpIds(contextHelpId, code, caller); String helpContextId = (contextHelpIds.length > 0) ? contextHelpIds[0] : null; // Show the message dialog finally result = doOpenMessageDialog(shell, title, message, buttonLabel, severity, dontAskAgainId, helpContextId, detailsText, detailsButtonIndex); } else { // Not interactive -> Re-pack the status and log it to the error log // Map any non-default status to IStatus.OK int newSeverity = severity; if (newSeverity != IStatus.OK && newSeverity != IStatus.INFO && newSeverity != IStatus.CANCEL && newSeverity != IStatus.WARNING && newSeverity != IStatus.ERROR) { newSeverity = IStatus.OK; } status = new Status(newSeverity, pluginId, code, message, exception); UIPlugin.getDefault().getLog().log(status); } } // Fill in the result object to the custom data object if (data == null && result != null) data = new PropertiesContainer(); if (data != null) data.setProperty(IStatusHandlerConstants.PROPERTY_RESULT, result); } catch (Throwable e) { error = e; } finally { // Invoke the callback if (done != null) { done.setResult(data); done.done(this, StatusHelper.getStatus(error)); } } return; } /** * Allows overrides to adjust the message severity based on the passed information finally * before the message box will show up. The default implementation will return whatever has * been passed in as proposed severity. * * @param proposedSeverity The proposed message severity. * @param exception The associated message exception. * @return The final message box title. Must not be <code>null</code>! */ protected int adjustSeverity(int proposedSeverity, Throwable exception) { return proposedSeverity; } /** * Allows subclasses to adjust the message box title based on the passed information finally * before the message box will show up. The default implementation will return whatever has * been passed in as proposed title. * * @param proposedTitle The proposed message box title. * @param severity The message severity. * @param exception The associated message exception. * * @return The final message box title. Must not be <code>null</code>! */ protected String adjustTitle(String proposedTitle, int severity, Throwable exception) { return proposedTitle; } /** * Allows subclasses to adjust the message box message based on the passed information finally * before the message box will show up. The default implementation will return whatever has * been passed in as proposed message. * * @param proposedMessage The proposed message box message. * @param severity The message severity. * @param exception The associated message exception. * @param caller The caller of the status handler or <code>null</code>. * * @return The final message box message. Must not be <code>null</code>! */ protected String adjustMessage(String proposedMessage, int severity, Throwable exception, Object caller) { return proposedMessage; } protected final static String[] EMPTY = new String[0]; /** * Allows subclasses to finally adjust the set of context help id's associated with message box. The * method must allows return a non <code>null</code> value! The default implementation only transforms * the proposed contextHelpId within an array. * <p> * Note: Only message with severity <code>ERROR</code> can have multiple context help id's associated! * <p> * @param proposedContextHelpId The proposed context help id. * @param errorCode The associated error code. * @param caller The caller of the status handler or <code>null</code>. * * @return An array of context help id's. Must not be <code>null</code>! */ protected String[] adjustContextHelpIds(String proposedContextHelpId, int errorCode, Object caller) { if (proposedContextHelpId == null) { return EMPTY; } return new String[] { proposedContextHelpId }; } /** * Open the message dialog. * * @param shell The shell. Must not be <code>null</code>. * @param title The title. Must not be <code>null</code>. * @param message The message. Must not be <code>null</code>. * @param buttonLabel An string array listing the labels of the message dialog buttons. If <code>null</code>, the default * labeling, typically "OK" for a single button message dialog, will be applied. * @param severity The severity. Must be one of the {@link IStatus} constants. * @param keyDontAskAgain The unique key for the stored result value or <code>null</code>. * @param helpContextId The help context id or <code>null</code>. * * @return {@link Integer} containing the id of the button pressed if the severity is {@link IStatusHandlerConstants#QUESTION} * or {@link IStatusHandlerConstants#YES_NO_CANCEL}, <code>null</code> otherwise. */ protected Object doOpenMessageDialog(Shell shell, String title, String message, String[] buttonLabel, int severity, String keyDontAskAgain, String helpContextId, String detailsText, int detailsButtonIndex) { Assert.isNotNull(shell); Assert.isNotNull(title); Assert.isNotNull(message); Object result = null; switch (severity) { case IStatusHandlerConstants.QUESTION: result = Integer.valueOf(OptionalMessageDialog.openYesNoDialog(shell, title, message, buttonLabel, keyDontAskAgain, helpContextId)); break; case IStatusHandlerConstants.YES_NO_CANCEL: result = Integer.valueOf(OptionalMessageDialog.openYesNoCancelDialog(shell, title, message, buttonLabel, keyDontAskAgain, helpContextId)); break; case IStatus.WARNING: OptionalMessageDialog.openWarningDialog(shell, title, message, buttonLabel, keyDontAskAgain, helpContextId); break; case IStatus.ERROR: OptionalMessageDialog.openErrorDialog(shell, title, message, buttonLabel, keyDontAskAgain, helpContextId, detailsText, detailsButtonIndex); break; default: OptionalMessageDialog.openInformationDialog(shell, title, message, buttonLabel, keyDontAskAgain, helpContextId); } return result; } }