org.eclipse.team.internal.ui.actions.TeamAction.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.team.internal.ui.actions.TeamAction.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2010 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.team.internal.ui.actions;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.List;

import org.eclipse.core.commands.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.core.TeamPlugin;
import org.eclipse.team.internal.ui.*;
import org.eclipse.ui.*;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.ide.ResourceUtil;

/**
 * The abstract superclass of all Team actions. This class contains some convenience
 * methods for getting selected objects and mapping selected objects to their
 * providers.
 * 
 * Team providers may subclass this class when creating their actions.
 * Team providers may also instantiate or subclass any of the  
 * subclasses of TeamAction provided in this package.
 */
public abstract class TeamAction extends AbstractHandler
        implements IObjectActionDelegate, IViewActionDelegate, IWorkbenchWindowActionDelegate, IActionDelegate2 {
    // The current selection
    private IStructuredSelection selection;

    // The shell, required for the progress dialog
    private Shell shell;

    // Constants for determining the type of progress. Subclasses may
    // pass one of these values to the run method.
    public final static int PROGRESS_DIALOG = 1;
    public final static int PROGRESS_BUSYCURSOR = 2;

    private IWorkbenchPart targetPart;
    private IWorkbenchWindow window;
    private IPartListener2 targetPartListener = new IPartListener2() {
        public void partActivated(IWorkbenchPartReference partRef) {
        }

        public void partBroughtToTop(IWorkbenchPartReference partRef) {
        }

        public void partClosed(IWorkbenchPartReference partRef) {
            if (targetPart == partRef.getPart(false)) {
                targetPart = null;
            }
        }

        public void partDeactivated(IWorkbenchPartReference partRef) {
        }

        public void partHidden(IWorkbenchPartReference partRef) {
        }

        public void partInputChanged(IWorkbenchPartReference partRef) {
        }

        public void partOpened(IWorkbenchPartReference partRef) {
        }

        public void partVisible(IWorkbenchPartReference partRef) {
        }
    };

    private ISelectionListener selectionListener = new ISelectionListener() {
        public void selectionChanged(IWorkbenchPart part, ISelection selection) {
            if (selection instanceof IStructuredSelection)
                TeamAction.this.selection = (IStructuredSelection) selection;
        }
    };

    /**
     * Creates an array of the given class type containing all the
     * objects in the selection that adapt to the given class.
     * 
     * @param selection
     * @param c
     * @return the selected adaptables
     */
    public static Object[] getSelectedAdaptables(ISelection selection, Class c) {
        ArrayList result = null;
        if (selection != null && !selection.isEmpty()) {
            result = new ArrayList();
            Iterator elements = ((IStructuredSelection) selection).iterator();
            while (elements.hasNext()) {
                Object adapter = getAdapter(elements.next(), c);
                if (c.isInstance(adapter)) {
                    result.add(adapter);
                }
            }
        }
        if (result != null && !result.isEmpty()) {
            return result.toArray((Object[]) Array.newInstance(c, result.size()));
        }
        return (Object[]) Array.newInstance(c, 0);
    }

    /**
     * Find the object associated with the given object when it is adapted to
     * the provided class. Null is returned if the given object does not adapt
     * to the given class
     * 
     * @param adaptable
     * @param c
     * @return Object
     */
    public static Object getAdapter(Object adaptable, Class c) {
        if (c.isInstance(adaptable)) {
            return adaptable;
        }
        if (adaptable instanceof IAdaptable) {
            IAdaptable a = (IAdaptable) adaptable;
            Object adapter = a.getAdapter(c);
            if (c.isInstance(adapter)) {
                return adapter;
            }
        }
        return null;
    }

    /**
     * Returns the selected projects.
     * 
     * @return the selected projects
     */
    protected IProject[] getSelectedProjects() {
        IResource[] selectedResources = getSelectedResources();
        if (selectedResources.length == 0)
            return new IProject[0];
        ArrayList projects = new ArrayList();
        for (int i = 0; i < selectedResources.length; i++) {
            IResource resource = selectedResources[i];
            if (resource.getType() == IResource.PROJECT) {
                projects.add(resource);
            }
        }
        return (IProject[]) projects.toArray(new IProject[projects.size()]);
    }

    /**
     * Returns an array of the given class type c that contains all
     * instances of c that are either contained in the selection or
     * are adapted from objects contained in the selection.
     * 
     * @param c
     * @return the selection adapted to the given class
     */
    protected Object[] getAdaptedSelection(Class c) {
        return getSelectedAdaptables(selection, c);
    }

    /**
     * Returns the selected resources.
     * 
     * @return the selected resources
     */
    protected IResource[] getSelectedResources() {
        return Utils.getContributedResources(getSelection().toArray());
    }

    protected IStructuredSelection getSelection() {
        if (selection == null)
            selection = StructuredSelection.EMPTY;
        return selection;
    }

    /**
      * Return the selected resource mappins that contain resources in 
      * projects that are associated with a repository of the given id.
      * @param providerId the repository provider id
      * @return the resource mappings that contain resources associated with the given provider
     */
    protected ResourceMapping[] getSelectedResourceMappings(String providerId) {
        Object[] elements = getSelection().toArray();
        ArrayList providerMappings = new ArrayList();
        for (int i = 0; i < elements.length; i++) {
            Object object = elements[i];
            Object adapted = getResourceMapping(object);
            if (adapted instanceof ResourceMapping) {
                ResourceMapping mapping = (ResourceMapping) adapted;
                if (providerId == null || isMappedToProvider(mapping, providerId)) {
                    providerMappings.add(mapping);
                }
            }
        }
        return (ResourceMapping[]) providerMappings.toArray(new ResourceMapping[providerMappings.size()]);
    }

    private Object getResourceMapping(Object object) {
        if (object instanceof ResourceMapping)
            return object;
        return Utils.getResourceMapping(object);
    }

    private boolean isMappedToProvider(ResourceMapping element, String providerId) {
        IProject[] projects = element.getProjects();
        for (int k = 0; k < projects.length; k++) {
            IProject project = projects[k];
            RepositoryProvider provider = RepositoryProvider.getProvider(project);
            if (provider != null && provider.getID().equals(providerId)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Convenience method for getting the current shell.
     * 
     * @return the shell
     */
    protected Shell getShell() {
        if (shell != null) {
            return shell;
        } else if (targetPart != null) {
            return targetPart.getSite().getShell();
        } else if (window != null) {
            return window.getShell();
        } else {
            IWorkbench workbench = TeamUIPlugin.getPlugin().getWorkbench();
            if (workbench == null)
                return null;
            IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
            if (window == null)
                return null;
            return window.getShell();
        }
    }

    /**
     * Convenience method for running an operation with progress and
     * error feedback.
     * 
     * @param runnable  the runnable which executes the operation
     * @param problemMessage  the message to display in the case of errors
     * @param progressKind  one of PROGRESS_BUSYCURSOR or PROGRESS_DIALOG
     */
    final protected void run(final IRunnableWithProgress runnable, final String problemMessage, int progressKind) {
        final Exception[] exceptions = new Exception[] { null };
        switch (progressKind) {
        case PROGRESS_BUSYCURSOR:
            BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                public void run() {
                    try {
                        runnable.run(new NullProgressMonitor());
                    } catch (InvocationTargetException e) {
                        exceptions[0] = e;
                    } catch (InterruptedException e) {
                        exceptions[0] = null;
                    }
                }
            });
            break;
        default:
        case PROGRESS_DIALOG:
            try {
                new ProgressMonitorDialog(getShell()).run(true, true, runnable);
            } catch (InvocationTargetException e) {
                exceptions[0] = e;
            } catch (InterruptedException e) {
                exceptions[0] = null;
            }
            break;
        }
        if (exceptions[0] != null) {
            handle(exceptions[0], null, problemMessage);
        }
    }

    /*
     * Method declared on IActionDelegate.
     */
    public void selectionChanged(IAction action, ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            this.selection = (IStructuredSelection) selection;
            if (action != null) {
                setActionEnablement(action);
            }
        }
    }

    /**
     * Method invoked from <code>selectionChanged(IAction, ISelection)</code> 
     * to set the enablement status of the action. The instance variable 
     * <code>selection</code> will contain the latest selection so the methods
     * <code>getSelectedResources()</code> and <code>getSelectedProjects()</code>
     * will provide the proper objects.
     * 
     * This method can be overridden by subclasses but should not be invoked by them.
     */
    protected void setActionEnablement(IAction action) {
        action.setEnabled(isEnabled());
    }

    /**
     * If an exception occurs during enablement testing, this method is invoked
     * to determine if the action should be enabled or not.
     * @param exception the exception
     * @return whether the action should be enabled or not
     */
    protected boolean isEnabledForException(TeamException exception) {
        if (exception.getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
            // Enable the action to allow the user to discover the problem
            return true;
        }
        // We should not open a dialog when determining menu enablement so log it instead
        TeamPlugin.log(exception);
        return false;
    }

    /*
     * Method declared on IObjectActionDelegate.
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
        if (targetPart != null) {
            this.shell = targetPart.getSite().getShell();
            this.targetPart = targetPart;
        }
    }

    /**
     * Shows the given errors to the user.
     * 
     * @param exception  the status containing the error
     * @param title  the title of the error dialog
     * @param message  the message for the error dialog
     */
    protected void handle(Exception exception, String title, String message) {
        Utils.handleError(getShell(), exception, title, message);
    }

    /**
     * Convenience method that maps the given resources to their providers.
     * The returned Hashtable has keys which are ITeamProviders, and values
     * which are Lists of IResources that are shared with that provider.
     * 
     * @return a hashtable mapping providers to their resources
     */
    protected Hashtable getProviderMapping(IResource[] resources) {
        Hashtable result = new Hashtable();
        for (int i = 0; i < resources.length; i++) {
            RepositoryProvider provider = RepositoryProvider.getProvider(resources[i].getProject());
            List list = (List) result.get(provider);
            if (list == null) {
                list = new ArrayList();
                result.put(provider, list);
            }
            list.add(resources[i]);
        }
        return result;
    }

    /**
     * @return IWorkbenchPart
     */
    protected IWorkbenchPart getTargetPart() {
        if (targetPart == null) {
            IWorkbenchPage page = TeamUIPlugin.getActivePage();
            if (page != null) {
                targetPart = page.getActivePart();
            }
        }
        return targetPart;

    }

    /**
     * Return the path that was active when the menu item was selected.
     * @return IWorkbenchPage
     */
    protected IWorkbenchPage getTargetPage() {
        if (getTargetPart() == null)
            return TeamUIPlugin.getActivePage();
        return getTargetPart().getSite().getPage();
    }

    /**
     * Show the view with the given ID in the perspective from which the action
     * was executed. Returns null if the view is not registered.
     * 
     * @param viewId
     * @return IViewPart
     */
    protected IViewPart showView(String viewId) {
        try {
            return getTargetPage().showView(viewId);
        } catch (PartInitException pe) {
            return null;
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart)
     */
    public void init(IViewPart view) {
        if (view != null) {
            this.shell = view.getSite().getShell();
            this.targetPart = view;
        }
    }

    public void init(IWorkbenchWindow window) {
        this.window = window;
        this.shell = window.getShell();
        window.getSelectionService().addPostSelectionListener(selectionListener);
        window.getActivePage().addPartListener(targetPartListener);
    }

    public IWorkbenchWindow getWindow() {
        return window;
    }

    public void dispose() {
        super.dispose();
        if (window != null) {
            window.getSelectionService().removePostSelectionListener(selectionListener);
            if (window.getActivePage() != null) {
                window.getActivePage().removePartListener(targetPartListener);
            }
            targetPartListener = null;
        }
        // Don't hold on to anything when we are disposed to prevent memory leaks (see bug 195521)
        selection = null;
        window = null;
        targetPart = null;
        shell = null;
    }

    /**
     * Actions must override to do their work.
     */
    protected abstract void execute(IAction action) throws InvocationTargetException, InterruptedException;

    /**
     * This method is called by the platform UI framework when a command is run for
     * which this action is the handler. The handler doesn't have an explicit context, for
     * example unlike a view, editor, or workbench window actions, they are not initialized
     * with a part. As a result when the action is run it will use the selection service
     * to determine to elements on which to perform the action.
     * <p>
     * CVS actions should ensure that they can run without a proxy action. Meaning that
     * <code>selectionChanged</code> and <code>run</code> should support passing
     * <code>null</code> as the IAction parameter.
     * </p>
     * @throws ExecutionException
     */
    public Object execute(ExecutionEvent event) throws ExecutionException {
        IWorkbenchWindow activeWorkbenchWindow = HandlerUtil.getActiveWorkbenchWindow(event);
        if (activeWorkbenchWindow != null) {
            ISelection selection = HandlerUtil.getCurrentSelection(event);
            if (selection != null) {
                IWorkbenchPart part = HandlerUtil.getActivePart(event);
                try {
                    execute(activeWorkbenchWindow, part, selection);
                } catch (InvocationTargetException e) {
                    throw new ExecutionException(TeamUIMessages.TeamAction_errorTitle, e);
                } catch (InterruptedException e) {
                    // Operation was canceled. Ignore
                }
            }
        }
        return null;
    }

    private void execute(IWorkbenchWindow activeWorkbenchWindow, IWorkbenchPart part, ISelection selection)
            throws InvocationTargetException, InterruptedException {
        // If the action is run from within an editor, try and find the
        // file for the given editor.
        if (part != null && part instanceof IEditorPart) {
            IEditorInput input = ((IEditorPart) part).getEditorInput();
            IFile file = ResourceUtil.getFile(input);
            if (file != null) {
                selectionChanged((IAction) null, new StructuredSelection(file));
            }
        } else {
            // Fallback is to prime the action with the selection
            selectionChanged((IAction) null, selection);
        }
        // Safe guard to ensure that the action is only run when enabled.
        if (isEnabled()) {
            execute((IAction) null);
        } else {
            MessageDialog.openInformation(activeWorkbenchWindow.getShell(),
                    TeamUIMessages.TeamAction_handlerNotEnabledTitle,
                    TeamUIMessages.TeamAction_handlerNotEnabledMessage);
        }
    }

    /**
     * Common run method for all Team actions.
     */
    public void run(IAction action) {
        try {
            execute(action);
        } catch (InvocationTargetException e) {
            // Handle the exception and any accumulated errors
            handle(e);
        } catch (InterruptedException e) {
            // Operation was canceled. Ignore.
        }
    }

    /**
     * This method can be overridden by subclasses but should not be invoked by
     * them.
     * 
     * @param e
     *            Exception to handle
     */
    protected void handle(Exception e) {
        handle(e, TeamUIMessages.TeamAction_errorTitle, null);
    }

    /**
     * The <code>TeamAction</code> implementation of this
     * <code>IActionDelegate2</code> method does nothing. Subclasses may
     * reimplement.
     */
    public void init(IAction action) {
    }

    /**
     * The <code>TeamAction</code> implementation of this
     * <code>IActionDelegate2</code> method redirects to the <code>run</code>
     * method. Subclasses may reimplement.
     */
    final public void runWithEvent(IAction action, Event event) {
        run(action);
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.commands.AbstractHandler#setEnabled(java.lang.Object)
     */
    public void setEnabled(Object evaluationContext) {
        IWorkbenchWindow activeWorkbenchWindow = (IWorkbenchWindow) HandlerUtil.getVariable(evaluationContext,
                ISources.ACTIVE_WORKBENCH_WINDOW_NAME);
        if (activeWorkbenchWindow != null) {
            ISelection selection = (ISelection) HandlerUtil.getVariable(evaluationContext,
                    ISources.ACTIVE_CURRENT_SELECTION_NAME);
            if (selection == null) {
                selection = StructuredSelection.EMPTY;
            }
            IWorkbenchPart part = (IWorkbenchPart) HandlerUtil.getVariable(evaluationContext,
                    ISources.ACTIVE_PART_NAME);
            updateSelection(part, selection);
        }
    }

    private void updateSelection(IWorkbenchPart part, ISelection selection) {
        // If the action is run from within an editor, try and find the
        // file for the given editor.
        setActivePart(null, part);
        if (part != null && part instanceof IEditorPart) {
            IEditorInput input = ((IEditorPart) part).getEditorInput();
            IFile file = ResourceUtil.getFile(input);
            if (file != null) {
                selectionChanged((IAction) null, new StructuredSelection(file));
            }
        } else {
            // Fallback is to prime the action with the selection
            selectionChanged((IAction) null, selection);
        }
    }

}