Package org.eclipse.ui.internal.operations

Source Code of org.eclipse.ui.internal.operations.AdvancedValidationUserApprover$StatusReportingRunnable

/*******************************************************************************
* Copyright (c) 2005, 2006 IBM Corporation 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:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.operations;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
import org.eclipse.core.commands.operations.IAdvancedUndoableOperation2;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationApprover2;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IAdaptable;
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.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.misc.StatusUtil;
import org.eclipse.ui.internal.util.Util;

/**
* <p>
* An operation approver that rechecks the validity of a proposed undo or redo
* operation using
* {@link IAdvancedUndoableOperation#computeUndoableStatus(IProgressMonitor)} or
* {@link IAdvancedUndoableOperation#computeRedoableStatus(IProgressMonitor)}.
* Some complex operations do not compute their validity in canUndo() or
* canRedo() because it is too time-consuming. To save time on complex
* validations, the true validity is not determined until it is time to perform
* the operation.
* </p>
* <p>
* Since 3.3, this operation approver also checks the validity of a proposed
* execute by determining whether the redo is viable.
*
* @since 3.1
*/
public class AdvancedValidationUserApprover implements IOperationApprover,
    IOperationApprover2 {
 
    /**
     * Static to prevent opening of error dialogs for automated testing.
     *
     * @since 3.3
     */
    public static boolean AUTOMATED_MODE = false;

  private IUndoContext context;

  private static final int EXECUTING = 1;

  private static final int UNDOING = 2;

  private static final int REDOING = 3;

  private class StatusReportingRunnable implements IRunnableWithProgress {
    IStatus status;

    int doing;

    IUndoableOperation operation;

    IOperationHistory history;

    IAdaptable uiInfo;

    StatusReportingRunnable(IUndoableOperation operation,
        IOperationHistory history, IAdaptable uiInfo, int doing) {
      super();
      this.operation = operation;
      this.history = history;
      this.doing = doing;
      this.uiInfo = uiInfo;
    }

    // The casts to IAdvancedUndoableOperation and
    // IAdvancedUndoableOperation2 are safe because these types were checked
    // in the call chain.
    public void run(IProgressMonitor pm) {
      try {
        switch (doing) {
        case UNDOING:
          status = ((IAdvancedUndoableOperation) operation)
              .computeUndoableStatus(pm);
          break;
        case REDOING:
          status = ((IAdvancedUndoableOperation) operation)
              .computeRedoableStatus(pm);
          break;
        case EXECUTING:
          status = ((IAdvancedUndoableOperation2) operation)
              .computeExecutionStatus(pm);
          break;
        }

      } catch (ExecutionException e) {
        reportException(e, uiInfo);
        status = IOperationHistory.OPERATION_INVALID_STATUS;
      }
    }

    IStatus getStatus() {
      return status;
    }
  }

  /**
   * Create an AdvancedValidationUserApprover that performs advanced
   * validations on proposed undo and redo operations for a given undo
   * context.
   *
   * @param context -
   *            the undo context of operations in question.
   */
  public AdvancedValidationUserApprover(IUndoContext context) {
    super();
    this.context = context;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation,
   *      org.eclipse.core.commands.operations.IOperationHistory,
   *      org.eclipse.core.runtime.IAdaptable)
   */
  public IStatus proceedRedoing(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo) {
    return proceedWithOperation(operation, history, uiInfo, REDOING);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.commands.operations.IOperationApprover#proceedUndoing(org.eclipse.core.commands.operations.IUndoableOperation,
   *      org.eclipse.core.commands.operations.IOperationHistory,
   *      org.eclipse.core.runtime.IAdaptable)
   */
  public IStatus proceedUndoing(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo) {

    return proceedWithOperation(operation, history, uiInfo, UNDOING);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.commands.operations.IOperationApprover2#proceedExecuting(org.eclipse.core.commands.operations.IUndoableOperation,
   *      org.eclipse.core.commands.operations.IOperationHistory,
   *      org.eclipse.core.runtime.IAdaptable)
   */
  public IStatus proceedExecuting(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo) {
    return proceedWithOperation(operation, history, uiInfo, EXECUTING);
  }

  /*
   * Determine whether the operation in question is still valid.
   */
  private IStatus proceedWithOperation(final IUndoableOperation operation,
      final IOperationHistory history, final IAdaptable uiInfo,
      final int doing) {

    // return immediately if the operation is not relevant
    if (!operation.hasContext(context)) {
      return Status.OK_STATUS;
    }

    // if the operation does not support advanced validation,
    // then we assume it is valid.
    if (doing == EXECUTING) {
      if (!(operation instanceof IAdvancedUndoableOperation2)) {
        return Status.OK_STATUS;
      }
    } else {
      if (!(operation instanceof IAdvancedUndoableOperation)) {
        return Status.OK_STATUS;
      }
    }

    // The next two methods make a number of UI calls, so we wrap the
    // whole thing up in a syncExec.
    final IStatus[] status = new IStatus[1];
    Workbench.getInstance().getDisplay().syncExec(new Runnable() {
      public void run() {
        // Compute the undoable or redoable status
        status[0] = computeOperationStatus(operation, history, uiInfo,
            doing);

        // Report non-OK statuses to the user. In some cases, the user
        // may choose to proceed, and the returned status will be
        // different than what is reported.
        if (!status[0].isOK()) {
          status[0] = reportAndInterpretStatus(status[0], uiInfo,
              operation, doing);
        }

      }
    });

    // If the operation is still not OK, inform the history that the
    // operation has changed, since it was previously believed to be valid.
    // We rely here on the ability of an IAdvancedUndoableOperation to
    // correctly report canUndo() and canRedo() once the undoable and
    // redoable status have been computed.
    if (!status[0].isOK()) {
      history.operationChanged(operation);
    }
    return status[0];
  }

  private IStatus computeOperationStatus(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo, int doing) {
    try {
      StatusReportingRunnable runnable = new StatusReportingRunnable(
          operation, history, uiInfo, doing);
      TimeTriggeredProgressMonitorDialog progressDialog = new TimeTriggeredProgressMonitorDialog(
          getShell(uiInfo), PlatformUI.getWorkbench()
              .getProgressService().getLongOperationTime());

      progressDialog.run(false, true, runnable);
      return runnable.getStatus();
    } catch (OperationCanceledException e) {
      return Status.CANCEL_STATUS;
    } catch (InvocationTargetException e) {
      reportException(e, uiInfo);
      return IOperationHistory.OPERATION_INVALID_STATUS;
    } catch (InterruptedException e) {
      // Operation was cancelled and acknowledged by runnable with this
      // exception. Do nothing.
      return Status.CANCEL_STATUS;
    }
  }

  /*
   * Report the specified execution exception to the log and to the user.
   */
  private void reportException(Exception e, IAdaptable uiInfo) {
    Throwable nestedException = StatusUtil.getCause(e);
    Throwable exception = (nestedException == null) ? e : nestedException;
    String title = WorkbenchMessages.Error;
    String message = WorkbenchMessages.WorkbenchWindow_exceptionMessage;
    String exceptionMessage = exception.getMessage();
    if (exceptionMessage == null) {
      exceptionMessage = message;
    }
    IStatus status = new Status(IStatus.ERROR,
        WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception);
    WorkbenchPlugin.log(message, status);

    boolean createdShell = false;
    Shell shell = getShell(uiInfo);
    if (shell == null) {
      createdShell = true;
      shell = new Shell();
    }
    ErrorDialog.openError(shell, title, message, status);
    if (createdShell) {
      shell.dispose();
    }
  }

  /*
   * Report a non-OK status to the user
   */
  private IStatus reportAndInterpretStatus(IStatus status, IAdaptable uiInfo,
      IUndoableOperation operation, int doing) {
    // Nothing to report if we are running automated tests.  We will treat
    // warnings as if they were authorized by the user.
    if (AUTOMATED_MODE) {
      if (status.getSeverity() == IStatus.WARNING) {
        return Status.OK_STATUS;
      }
      return status;
    }
   
    // CANCEL status is assumed to be initiated by the user, so there
    // is nothing to report.
    if (status.getSeverity() == IStatus.CANCEL) {
      return status;
    }

    // Other status severities are reported with a message dialog.
    // First obtain a shell and set up the dialog title.
    boolean createdShell = false;
    IStatus reportedStatus = status;

    Shell shell = getShell(uiInfo);
    if (shell == null) {
      createdShell = true;
      shell = new Shell();
    }

    // Set up the dialog. For non-error statuses, we use a warning dialog
    // that allows the user to proceed or to cancel out of the operation.

    if (!(status.getSeverity() == IStatus.ERROR)) {
      String warning, title;
      switch (doing) {
      case UNDOING:
        warning = WorkbenchMessages.Operations_proceedWithNonOKUndoStatus;
        if (status.getSeverity() == IStatus.INFO) {
          title = WorkbenchMessages.Operations_undoInfo;
        } else {
          title = WorkbenchMessages.Operations_undoWarning;
        }
        break;
      case REDOING:
        warning = WorkbenchMessages.Operations_proceedWithNonOKRedoStatus;
        if (status.getSeverity() == IStatus.INFO) {
          title = WorkbenchMessages.Operations_redoInfo;
        } else {
          title = WorkbenchMessages.Operations_redoWarning;
        }
        break;
      default: // EXECUTING
        warning = WorkbenchMessages.Operations_proceedWithNonOKExecuteStatus;
        if (status.getSeverity() == IStatus.INFO) {
          title = WorkbenchMessages.Operations_executeInfo;
        } else {
          title = WorkbenchMessages.Operations_executeWarning;
        }
        break;
      }

      String message = NLS.bind(warning, new Object[] { status.getMessage(), operation.getLabel() });
      String[] buttons = new String[] { IDialogConstants.YES_LABEL,
          IDialogConstants.NO_LABEL };
      MessageDialog dialog = new MessageDialog(shell, title, null,
          message, MessageDialog.WARNING, buttons, 0);
      int dialogAnswer = dialog.open();
      // The user has been given the specific status and has chosen
      // to proceed or to cancel. The user choice determines what
      // the status should be at this point, OK or CANCEL.
      if (dialogAnswer == Window.OK) {
        reportedStatus = Status.OK_STATUS;
      } else {
        reportedStatus = Status.CANCEL_STATUS;
      }
    } else {
      String title, stopped;
      switch (doing) {
      case UNDOING:
        title = WorkbenchMessages.Operations_undoProblem;
        stopped = WorkbenchMessages.Operations_stoppedOnUndoErrorStatus;
        break;
      case REDOING:
        title = WorkbenchMessages.Operations_redoProblem;
        stopped = WorkbenchMessages.Operations_stoppedOnRedoErrorStatus;
        break;
      default: // EXECUTING
        title = WorkbenchMessages.Operations_executeProblem;
        stopped = WorkbenchMessages.Operations_stoppedOnExecuteErrorStatus;

        break;
      }

      // It is an error condition. The user has no choice to proceed, so
      // we only report what has gone on. We use a warning icon instead of
      // an error icon since there has not yet been a failure.

      String message = NLS.bind(stopped, status.getMessage(), operation
          .getLabel());

      MessageDialog dialog = new MessageDialog(shell, title, null,
          message, MessageDialog.WARNING,
          new String[] { IDialogConstants.OK_LABEL }, 0); // ok
      dialog.open();
    }

    if (createdShell) {
      shell.dispose();
    }

    return reportedStatus;

  }

  /*
   * Return the shell described by the supplied uiInfo, or null if no shell is
   * described.
   */
  Shell getShell(IAdaptable uiInfo) {
    if (uiInfo != null) {
      Shell shell = (Shell) Util.getAdapter(uiInfo, Shell.class);
      if (shell != null) {
        return shell;
      }
    }
    return null;
  }
}
TOP

Related Classes of org.eclipse.ui.internal.operations.AdvancedValidationUserApprover$StatusReportingRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.