org.eclipse.recommenders.internal.snipmatch.rcp.SnippetsView.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.recommenders.internal.snipmatch.rcp.SnippetsView.java

Source

/**
 * Copyright (c) 2010, 2014 Darmstadt University of Technology.
 * 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:
 *    Marcel Bruch - initial API and implementation.
 *    Olav Lenz - change list viewer to tree viewer
 */
package org.eclipse.recommenders.internal.snipmatch.rcp;

import static com.google.common.base.Optional.*;
import static java.text.MessageFormat.format;
import static org.eclipse.recommenders.internal.snipmatch.rcp.SnipmatchRcpModule.REPOSITORY_CONFIGURATION_FILE;
import static org.eclipse.recommenders.rcp.SharedImages.Images.*;
import static org.eclipse.recommenders.utils.Checks.*;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.inject.Inject;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.databinding.viewers.IViewerObservableList;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.recommenders.internal.snipmatch.rcp.Repositories.SnippetRepositoryConfigurationChangedEvent;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.LogMessages;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.Messages;
import org.eclipse.recommenders.rcp.IRcpService;
import org.eclipse.recommenders.rcp.SharedImages;
import org.eclipse.recommenders.rcp.SharedImages.ImageResource;
import org.eclipse.recommenders.rcp.SharedImages.Images;
import org.eclipse.recommenders.rcp.utils.Jobs;
import org.eclipse.recommenders.snipmatch.ISnippet;
import org.eclipse.recommenders.snipmatch.ISnippetRepository;
import org.eclipse.recommenders.snipmatch.SearchContext;
import org.eclipse.recommenders.snipmatch.Snippet;
import org.eclipse.recommenders.snipmatch.model.SnippetRepositoryConfiguration;
import org.eclipse.recommenders.snipmatch.rcp.ISnippetRepositoryWizard;
import org.eclipse.recommenders.snipmatch.rcp.SnippetEditor;
import org.eclipse.recommenders.snipmatch.rcp.SnippetEditorInput;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryClosedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryContentChangedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryOpenedEvent;
import org.eclipse.recommenders.snipmatch.rcp.model.SnippetRepositoryConfigurations;
import org.eclipse.recommenders.utils.Logs;
import org.eclipse.recommenders.utils.Nonnull;
import org.eclipse.recommenders.utils.Nullable;
import org.eclipse.recommenders.utils.Recommendation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.name.Named;

public class SnippetsView extends ViewPart implements IRcpService {

    public static final String SEARCH_FIELD = "org.eclipse.recommenders.snipmatch.rcp.snippetsview.searchfield"; //$NON-NLS-1$
    public static final String TREE = "org.eclipse.recommenders.snipmatch.rcp.snippetsview.tree"; //$NON-NLS-1$
    public static final String SWT_ID = "org.eclipse.swtbot.widget.key"; //$NON-NLS-1$

    private Text txtSearch;
    private TreeViewer treeViewer;
    private Tree tree;

    private IViewerObservableList selection;

    private SharedImages images;

    private final Repositories repos;
    private final SnippetRepositoryConfigurations configs;
    private final File repositoryConfigurationFile;
    private final EventBus bus;
    private final SnipmatchRcpPreferences prefs;

    private Action addRepositoryAction;
    private Action removeRepositoryAction;
    private Action editRepositoryAction;
    private Action enableRepositoryAction;
    private Action disableRepositoryAction;

    private Action refreshAction;

    private Action addSnippetAction;
    private Action removeSnippetAction;
    private Action editSnippetAction;
    private Action shareSnippetAction;

    private volatile boolean initializeTableData = true;

    private volatile List<SnippetRepositoryConfiguration> availableRepositories = Lists.newArrayList();
    private volatile ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> snippetsGroupedByRepoName = LinkedListMultimap
            .create();
    private volatile ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> filteredSnippetsGroupedByRepoName = LinkedListMultimap
            .create();

    private final Function<KnownSnippet, String> toStringRepresentation = new Function<KnownSnippet, String>() {

        @Override
        public String apply(KnownSnippet input) {
            return SnippetProposals.createDisplayString(input.snippet);
        }
    };

    @Inject
    public SnippetsView(Repositories repos, SharedImages images, SnippetRepositoryConfigurations configs,
            EventBus bus, @Named(REPOSITORY_CONFIGURATION_FILE) File repositoryConfigurationFile,
            SnipmatchRcpPreferences prefs) {
        this.repos = repos;
        this.configs = configs;
        this.images = images;
        this.bus = bus;
        this.repositoryConfigurationFile = repositoryConfigurationFile;
        this.prefs = prefs;
    }

    @Override
    public void createPartControl(final Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        GridLayoutFactory.swtDefaults().applyTo(composite);

        txtSearch = new Text(composite, SWT.BORDER | SWT.ICON_SEARCH | SWT.SEARCH | SWT.CANCEL);
        txtSearch.setMessage(Messages.SEARCH_PLACEHOLDER_SEARCH_TEXT);
        GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(1, 1).applyTo(txtSearch);
        txtSearch.addModifyListener(new ModifyListener() {

            @Override
            public void modifyText(ModifyEvent e) {
                refreshUI();
            }
        });
        txtSearch.setData(SWT_ID, SEARCH_FIELD);
        txtSearch.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.ARROW_DOWN && tree.getItemCount() != 0) {
                    tree.setFocus();
                    tree.setSelection(tree.getTopItem());
                }
            }
        });

        Composite treeComposite = new Composite(composite, SWT.NONE);

        TreeColumnLayout treeLayout = new TreeColumnLayout();
        treeComposite.setLayout(treeLayout);
        treeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

        treeViewer = new TreeViewer(treeComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL);
        ColumnViewerToolTipSupport.enableFor(treeViewer);
        tree = treeViewer.getTree();
        tree.setData(SWT_ID, TREE);
        tree.setHeaderVisible(true);
        tree.setLinesVisible(true);

        TreeViewerColumn snippetViewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        TreeColumn snippetColumn = snippetViewerColumn.getColumn();
        treeLayout.setColumnData(snippetColumn, new ColumnWeightData(1, ColumnWeightData.MINIMUM_WIDTH, true));
        snippetColumn.setText(Messages.TABLE_COLUMN_TITLE_SNIPPETS);
        snippetViewerColumn.setLabelProvider(new StyledCellLabelProvider() {

            @Override
            public void update(ViewerCell cell) {
                Object element = cell.getElement();
                StyledString text = new StyledString();
                if (element instanceof SnippetRepositoryConfiguration) {
                    SnippetRepositoryConfiguration config = (SnippetRepositoryConfiguration) element;

                    text.append(config.getName());
                    text.append(" "); //$NON-NLS-1$

                    if (prefs.isRepositoryEnabled(config)) {
                        text.append(format(Messages.TABLE_CELL_SUFFIX_SNIPPETS, fetchNumberOfSnippets(config)),
                                StyledString.COUNTER_STYLER);
                    } else {
                        text.append(Messages.TABLE_REPOSITORY_DISABLED, StyledString.COUNTER_STYLER);
                    }

                    cell.setImage(images.getImage(Images.OBJ_REPOSITORY));
                } else if (element instanceof KnownSnippet) {
                    KnownSnippet knownSnippet = (KnownSnippet) element;
                    text.append(toStringRepresentation.apply(knownSnippet));
                }
                cell.setText(text.toString());
                cell.setStyleRanges(text.getStyleRanges());
                super.update(cell);
            }
        });

        treeViewer.addOpenListener(new IOpenListener() {

            @Override
            public void open(OpenEvent event) {
                if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
                    editSnippets();
                }
            }
        });
        treeViewer.setUseHashlookup(true);
        treeViewer.setContentProvider(new ILazyTreeContentProvider() {

            @Override
            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            }

            @Override
            public void dispose() {
            }

            @Override
            public void updateElement(Object parent, int index) {
                if (parent instanceof IViewSite) {
                    SnippetRepositoryConfiguration config = availableRepositories.get(index);
                    treeViewer.replace(parent, index, config);
                    treeViewer.setChildCount(config, getChildren(config).length);
                } else if (parent instanceof SnippetRepositoryConfiguration) {
                    treeViewer.replace(parent, index, getChildren(parent)[index]);
                }
            }

            private Object[] getChildren(Object element) {
                if (element instanceof SnippetRepositoryConfiguration) {
                    return filteredSnippetsGroupedByRepoName.get((SnippetRepositoryConfiguration) element)
                            .toArray();
                }
                return new Object[0];
            }

            @Override
            public void updateChildCount(Object element, int currentChildCount) {
                int count = 0;

                if (element instanceof IViewSite) {
                    count = availableRepositories.size();
                }

                if (availableRepositories.contains(element)) {
                    count = getChildren(element).length;
                }

                if (count != currentChildCount) {
                    treeViewer.setChildCount(element, count);
                }
            }

            @Override
            public Object getParent(Object element) {
                if (element instanceof KnownSnippet) {
                    KnownSnippet knownSnippet = (KnownSnippet) element;
                    return knownSnippet.config;
                }
                return null;
            }
        });

        selection = ViewersObservables.observeMultiSelection(treeViewer);

        createActions(parent);
        addToolBar(parent);
        addContextMenu();

        refreshUI();
    }

    private int fetchNumberOfSnippets(SnippetRepositoryConfiguration config) {
        return snippetsGroupedByRepoName.get(config).size();
    }

    private void createActions(final Composite parent) {
        addRepositoryAction = new Action() {

            @Override
            public void run() {
                addRepo();
            }
        };

        removeRepositoryAction = new Action() {
            @Override
            public void run() {
                removeRepos();
            }
        };

        editRepositoryAction = new Action() {
            @Override
            public void run() {
                editRepos();
            }
        };

        enableRepositoryAction = new Action() {
            @Override
            public void run() {
                changeEnableStateRepos(true);
            }
        };

        disableRepositoryAction = new Action() {
            @Override
            public void run() {
                changeEnableStateRepos(false);
            }
        };

        refreshAction = new Action() {
            @Override
            public void run() {
                reload();
            }
        };

        addSnippetAction = new Action() {
            @Override
            public void run() {
                addSnippet();
            }
        };

        removeSnippetAction = new Action() {
            @Override
            public void run() {
                removeSnippets();
            }
        };

        editSnippetAction = new Action() {
            @Override
            public void run() {
                editSnippets();
            }
        };

        shareSnippetAction = new Action() {
            @Override
            public void run() {
                shareSnippets();
            };
        };

        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                updateActionsStatus();
            }

        });

        updateActionsStatus();

    }

    private void updateActionsStatus() {
        boolean removeRepoEnabled = false;
        boolean editRepoEnabled = false;
        boolean enableRepoEnabled = false;
        boolean disableRepoEnabled = false;

        boolean removeSnippetEnabled = false;
        boolean editSnippetEnabled = false;
        boolean shareSnippetEnabled = false;

        if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
            SnippetRepositoryConfiguration selectedConfig = (SnippetRepositoryConfiguration) selection.get(0);
            editRepoEnabled = WizardDescriptors.isWizardAvailable(selectedConfig)
                    && !selectionContainsDefaultConfigurations();
        }

        if (selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class)
                && !selectionContainsDefaultConfigurations()) {
            removeRepoEnabled = true;
        }

        if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
            SnippetRepositoryConfiguration config = (SnippetRepositoryConfiguration) selection.get(0);

            boolean repositoryEnabled = prefs.isRepositoryEnabled(config);
            enableRepoEnabled = !repositoryEnabled;
            disableRepoEnabled = repositoryEnabled;
        }

        if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
            removeSnippetEnabled = true;
            editSnippetEnabled = true;
        }
        if (selectionIsShareable()) {
            shareSnippetEnabled = true;
        }

        removeRepositoryAction.setEnabled(removeRepoEnabled);
        editRepositoryAction.setEnabled(editRepoEnabled);
        enableRepositoryAction.setEnabled(enableRepoEnabled);
        disableRepositoryAction.setEnabled(disableRepoEnabled);

        removeSnippetAction.setEnabled(removeSnippetEnabled);
        editSnippetAction.setEnabled(editSnippetEnabled);
        shareSnippetAction.setEnabled(shareSnippetEnabled);
    }

    private boolean selectionContainsDefaultConfigurations() {
        List<SnippetRepositoryConfiguration> configs = castSelection();
        for (SnippetRepositoryConfiguration config : configs) {
            if (config.isDefaultConfiguration()) {
                return true;
            }
        }
        return false;
    }

    private void addRepo() {
        List<WizardDescriptor> availableWizards = WizardDescriptors.loadAvailableWizards();
        if (!availableWizards.isEmpty()) {
            SnippetRepositoryTypeSelectionWizard newWizard = new SnippetRepositoryTypeSelectionWizard();
            WizardDialog dialog = new WizardDialog(tree.getShell(), newWizard);
            if (dialog.open() == Window.OK) {
                SnippetRepositoryConfiguration newConfiguration = newWizard.getConfiguration();
                newConfiguration.setId(UUID.randomUUID().toString());
                configs.getRepos().add(newConfiguration);
                RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
                bus.post(new Repositories.SnippetRepositoryConfigurationChangedEvent());
                refreshUI();
            }
        }
    }

    private void removeRepos() {
        ensureIsTrue(selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class));

        List<SnippetRepositoryConfiguration> configurations = castSelection();

        for (SnippetRepositoryConfiguration config : configurations) {
            MessageDialogWithToggle confirmDialog = MessageDialogWithToggle.openOkCancelConfirm(tree.getShell(),
                    Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_TITLE,
                    format(Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_MESSAGE, config.getName()),
                    Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_TOGGLE_MESSAGE, true, null, null);

            boolean confirmed = confirmDialog.getReturnCode() == Status.OK;
            if (!confirmed) {
                return;
            }

            boolean delete = confirmDialog.getToggleState();
            if (delete) {
                ISnippetRepository repo = repos.getRepository(config.getId()).orNull();
                if (repo != null) {
                    repo.delete();
                }
            }

            configs.getRepos().remove(config);
        }

        RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
        bus.post(new Repositories.SnippetRepositoryConfigurationChangedEvent());
        refreshUI();
    }

    private void editRepos() {
        ensureIsTrue(selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class));

        SnippetRepositoryConfiguration oldConfiguration = cast(selection.get(0));
        List<WizardDescriptor> suitableWizardDescriptors = WizardDescriptors
                .filterApplicableWizardDescriptors(WizardDescriptors.loadAvailableWizards(), oldConfiguration);
        if (!suitableWizardDescriptors.isEmpty()) {

            ISnippetRepositoryWizard wizard;
            if (suitableWizardDescriptors.size() == 1) {
                wizard = Iterables.getOnlyElement(suitableWizardDescriptors).getWizard();
                wizard.setConfiguration(oldConfiguration);
            } else {
                wizard = new SnippetRepositoryTypeSelectionWizard(oldConfiguration);
            }

            WizardDialog dialog = new WizardDialog(tree.getShell(), wizard);
            if (dialog.open() == Window.OK) {
                List<SnippetRepositoryConfiguration> configurations = configs.getRepos();
                configurations.add(configurations.indexOf(oldConfiguration), wizard.getConfiguration());
                configurations.remove(oldConfiguration);
                RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
                bus.post(new SnippetRepositoryConfigurationChangedEvent());
                refreshUI();
            }
        }
    }

    private void changeEnableStateRepos(boolean enabled) {
        ensureIsTrue(selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class));

        SnippetRepositoryConfiguration config = cast(selection.get(0));
        prefs.setRepositoryEnabled(config, enabled);
        updateActionsStatus();
    }

    private void reload() {
        Job reconnectJob = new Job(Messages.JOB_NAME_RECONNECTING_SNIPPET_REPOSITORY) {

            @Override
            protected IStatus run(IProgressMonitor monitor) {
                PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        refreshAction.setEnabled(false);
                    }
                });
                try {
                    repos.reload();
                } catch (Exception e) {
                    // Snipmatch's default repositories cannot throw an
                    // IOException here
                    Logs.log(LogMessages.ERROR_FAILED_TO_RELOAD_REPOSITORIES, e);
                }
                PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        refreshAction.setEnabled(true);
                    }
                });
                return Status.OK_STATUS;
            }
        };
        reconnectJob.schedule();
    }

    private void addSnippet(ISnippetRepository repo) {
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

        try {
            ISnippet snippet = new Snippet();

            final SnippetEditorInput input = new SnippetEditorInput(snippet, repo);
            SnippetEditor editor = cast(
                    page.openEditor(input, "org.eclipse.recommenders.snipmatch.rcp.editors.snippet")); //$NON-NLS-1$
            // mark the editor dirty when opening a newly created snippet
            editor.markDirtyUponSnippetCreation();
        } catch (Exception e) {
            Throwables.propagate(e);
        }
    }

    private void addSnippet() {
        addSnippet(null);
    }

    private void removeSnippets() {
        ensureIsTrue(selectionConsistsOnlyElementsOf(KnownSnippet.class));

        boolean confirmed = MessageDialog.openConfirm(tree.getShell(), Messages.CONFIRM_DIALOG_DELETE_SNIPPET_TITLE,
                Messages.CONFIRM_DIALOG_DELETE_SNIPPET_MESSAGE);

        if (!confirmed) {
            return;
        }

        List<KnownSnippet> selectedSnippets = castSelection();

        for (KnownSnippet knownSnippet : selectedSnippets) {
            try {
                for (ISnippetRepository repo : repos.getRepositories()) {
                    repo.delete(knownSnippet.snippet.getUuid());
                }
            } catch (Exception e) {
                Throwables.propagate(e);
            }
        }
    }

    private void editSnippets() {
        ensureIsTrue(selectionConsistsOnlyElementsOf(KnownSnippet.class));

        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

        List<KnownSnippet> selectedSnippets = castSelection();
        for (KnownSnippet knownSnippet : selectedSnippets) {
            try {
                ISnippet snippet = knownSnippet.snippet;
                ISnippetRepository repository = findRepoForOriginalSnippet(snippet);

                final SnippetEditorInput input = new SnippetEditorInput(snippet, repository);
                page.openEditor(input, "org.eclipse.recommenders.snipmatch.rcp.editors.snippet"); //$NON-NLS-1$
            } catch (Exception e) {
                Throwables.propagate(e);
            }
        }
    }

    private void shareSnippets() {
        ensureIsTrue(selectionIsShareable());

        List<KnownSnippet> selectedSnippets = castSelection();
        Optional<ISnippetRepository> repository = repos.getRepository(selectedSnippets.get(0).config.getId());
        if (!repository.isPresent()) {
            return;
        }
        Collection<UUID> uuids = Collections2.transform(selectedSnippets, new Function<KnownSnippet, UUID>() {
            @Override
            public UUID apply(KnownSnippet input) {
                return input.snippet.getUuid();
            }
        });
        repository.get().share(uuids);
    }

    private void addToolBar(final Composite parent) {
        IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();

        addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET, ELCL_ADD_SNIPPET, toolBarManager, addSnippetAction);

        addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, toolBarManager,
                addRepositoryAction);

        toolBarManager.add(new Separator());
        addAction(Messages.TOOLBAR_TOOLTIP_EXPAND_ALL, ELCL_EXPAND_ALL, toolBarManager, new Action() {
            @Override
            public void run() {
                for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) {
                    treeViewer.getTree().getItem(i).setExpanded(true);
                }
            }

        });

        addAction(Messages.TOOLBAR_TOOLTIP_COLLAPSE_ALL, ELCL_COLLAPSE_ALL, toolBarManager, new Action() {
            @Override
            public void run() {
                for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) {
                    treeViewer.getTree().getItem(i).setExpanded(false);
                }
            }
        });

        toolBarManager.add(new Separator());

        addAction(Messages.SNIPPETS_VIEW_MENUITEM_REFRESH, ELCL_REFRESH, toolBarManager, refreshAction);
    }

    private void addContextMenu() {
        final MenuManager menuManager = new MenuManager();
        Menu contextMenu = menuManager.createContextMenu(tree);
        menuManager.setRemoveAllWhenShown(true);
        tree.setMenu(contextMenu);

        menuManager.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager manager) {
                boolean addedAddSnippetToRepoAction = false;

                SnippetRepositoryConfiguration guessedConfiguration = guessConfigurationForNewSnippet().orNull();
                if (guessedConfiguration != null) {
                    final ISnippetRepository repo = repos.getRepository(guessedConfiguration.getId()).orNull();
                    if (repo != null) {
                        addAction(format(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET_TO_REPOSITORY,
                                guessedConfiguration.getName()), ELCL_ADD_SNIPPET, manager, new Action() {
                                    @Override
                                    public void run() {
                                        addSnippet(repo);
                                    }
                                });
                        addedAddSnippetToRepoAction = true;
                    }
                }

                if (!addedAddSnippetToRepoAction) {
                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET, ELCL_ADD_SNIPPET, manager,
                            addSnippetAction);
                }

                if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {

                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_EDIT_SNIPPET, ELCL_EDIT_SNIPPET, manager,
                            editSnippetAction);
                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_REMOVE_SNIPPET, ELCL_REMOVE_SNIPPET, manager,
                            removeSnippetAction);
                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_SHARE_SNIPPET, ELCL_SHARE_SNIPPET, manager,
                            shareSnippetAction);
                }

                manager.add(new Separator());

                addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, manager,
                        addRepositoryAction);

                if (selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class)) {
                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_EDIT_REPOSITORY, ELCL_EDIT_REPOSITORY, manager,
                            editRepositoryAction);
                    addAction(Messages.SNIPPETS_VIEW_MENUITEM_REMOVE_REPOSITORY, ELCL_REMOVE_REPOSITORY,
                            ELCL_REMOVE_REPOSITORY_DISABLED, manager, removeRepositoryAction);

                    if (disableRepositoryAction.isEnabled()) {
                        addAction(Messages.SNIPPETS_VIEW_MENUITEM_DISABLE_REPOSITORY, ELCL_DISABLE_REPOSITORY,
                                manager, disableRepositoryAction);
                    }

                    if (enableRepositoryAction.isEnabled()) {
                        addAction(Messages.SNIPPETS_VIEW_MENUITEM_ENABLE_REPOSITORY, ELCL_ENABLE_REPOSITORY,
                                manager, enableRepositoryAction);
                    }
                }

            }

        });
    }

    protected Optional<SnippetRepositoryConfiguration> guessConfigurationForNewSnippet() {
        if (selection.isEmpty()) {
            return absent();
        }

        SnippetRepositoryConfiguration selectedConfiguration = null;

        if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
            selectedConfiguration = cast(selection.get(0));
        } else if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
            List<KnownSnippet> selectedSnippets = castSelection();

            for (KnownSnippet snippet : selectedSnippets) {
                if (selectedConfiguration == null) {
                    selectedConfiguration = snippet.config;
                } else if (!selectedConfiguration.equals(snippet.config)) {
                    return absent();
                }
            }
        }
        return fromNullable(selectedConfiguration);
    }

    private <T> boolean selectionContainsOnlyOneElementOf(Class<T> aClass) {
        return selection != null && selection.size() == 1 && aClass.isAssignableFrom(selection.get(0).getClass());
    }

    private <T> boolean selectionConsistsOnlyElementsOf(Class<T> aClass) {
        if (selection == null || selection.isEmpty()) {
            return false;
        }
        for (Object element : selection) {
            if (!aClass.isAssignableFrom(element.getClass())) {
                return false;
            }
        }
        return true;
    }

    private boolean selectionIsShareable() {
        if (selection == null || selection.isEmpty()) {
            return false;
        }
        if (!selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
            return false;
        }
        Set<String> configIds = Sets.newHashSet();
        for (Object element : selection) {
            KnownSnippet snippet = (KnownSnippet) element;
            String configId = snippet.config.getId();
            configIds.add(configId);
            if (configIds.size() > 1) {
                return false;
            }
            Optional<ISnippetRepository> repository = repos.getRepository(configId);
            if (!repository.isPresent()) {
                return false;
            }
            if (!repository.get().isSharingSupported()) {
                return false;
            }
        }
        return true;
    }

    private <T> List<T> castSelection() {
        List<T> result = Lists.newArrayList();
        for (Object element : selection) {
            T casted = cast(element);
            result.add(casted);
        }
        return result;
    }

    private void addAction(String text, ImageResource imageResource, IContributionManager contributionManager,
            IAction action) {
        action.setImageDescriptor(images.getDescriptor(imageResource));
        action.setText(text);
        action.setToolTipText(text);
        contributionManager.add(action);
    }

    private void addAction(String text, Images imageResource, Images imageResourceDisabled,
            IContributionManager contributionManager, Action action) {
        action.setDisabledImageDescriptor(images.getDescriptor(imageResourceDisabled));
        addAction(text, imageResource, contributionManager, action);
    }

    @Subscribe
    public void onEvent(SnippetRepositoryOpenedEvent e) throws IOException {
        refreshUI();
    }

    @Subscribe
    public void onEvent(SnippetRepositoryClosedEvent e) throws IOException {
        refreshUI();
    }

    @Subscribe
    public void onEvent(SnippetRepositoryContentChangedEvent e) throws IOException {
        refreshUI();
    }

    private void refreshUI() {
        if (!repos.isRunning()) {
            return;
        }
        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {

            @Override
            public void run() {
                Job.getJobManager().cancel(SearchJob.SEARCH_JOB_FAMILY);
                Job.getJobManager().cancel(RefreshTableJob.REFRESH_TABLE_JOB_FAMILY);
                final String query = txtSearch.isDisposed() ? "" : txtSearch.getText(); //$NON-NLS-1$
                Job searchSnippetsJob = new SearchJob(Messages.JOB_NAME_SEARCHING_SNIPPET_REPOSITORIES, query);
                Job refreshTableJob = new RefreshTableJob(Messages.JOB_NAME_REFRESHING_SNIPPETS_VIEW);
                Jobs.sequential(Messages.JOB_GROUP_UPDATING_SNIPPETS_VIEW, searchSnippetsJob, refreshTableJob);
            }
        });
    }

    @Nullable
    private ISnippetRepository findRepoForOriginalSnippet(ISnippet snippet) {
        for (ISnippetRepository repo : repos.getRepositories()) {
            if (repo.hasSnippet(snippet.getUuid())) {
                return repo;
            }
        }
        return null;
    }

    @Override
    public void setFocus() {
        treeViewer.getControl().setFocus();
    }

    private boolean isImportSupported() {
        for (ISnippetRepository repo : repos.getRepositories()) {
            if (repo.isImportSupported()) {
                return true;
            }
        }
        return false;
    }

    private final class SearchJob extends Job {

        public static final String SEARCH_JOB_FAMILY = "SEARCH_JOB_FAMILY"; //$NON-NLS-1$

        private final String query;

        private SearchJob(String name, String query) {
            super(name);
            this.query = query;
            setSystem(true);
        }

        @Override
        public IStatus run(@Nonnull IProgressMonitor monitor) {
            SubMonitor progress = SubMonitor.convert(monitor, Messages.MONITOR_SEARCH_SNIPPETS, 3);
            try {
                snippetsGroupedByRepoName = searchSnippets("", progress.newChild(1)); //$NON-NLS-1$
                filteredSnippetsGroupedByRepoName = searchSnippets(query, progress.newChild(1));
                availableRepositories = configs.getRepos();
                progress.worked(1);
                if (monitor.isCanceled()) {
                    return Status.CANCEL_STATUS;
                }
                return Status.OK_STATUS;
            } finally {
                monitor.done();
            }
        }

        private ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> searchSnippets(String searchTerm,
                IProgressMonitor monitor) {

            try {
                ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> snippetsGroupedByRepositoryName = LinkedListMultimap
                        .create();

                monitor.beginTask(Messages.MONITOR_SEARCH_SNIPPETS, configs.getRepos().size());
                for (SnippetRepositoryConfiguration config : configs.getRepos()) {
                    if (monitor.isCanceled()) {
                        return snippetsGroupedByRepositoryName;
                    }
                    ISnippetRepository repo = repos.getRepository(config.getId()).orNull();
                    if (repo == null) {
                        continue;
                    }
                    Set<KnownSnippet> knownSnippets = Sets.newHashSet();
                    for (Recommendation<ISnippet> recommendation : repo
                            .search(new SearchContext(searchTerm.trim()))) {
                        if (monitor.isCanceled()) {
                            return snippetsGroupedByRepositoryName;
                        }
                        knownSnippets.add(new KnownSnippet(config, recommendation.getProposal()));
                    }
                    List<KnownSnippet> sorted = Ordering.from(String.CASE_INSENSITIVE_ORDER)
                            .onResultOf(toStringRepresentation).sortedCopy(knownSnippets);
                    snippetsGroupedByRepositoryName.putAll(config, sorted);
                    monitor.worked(1);
                }

                return snippetsGroupedByRepositoryName;
            } finally {
                monitor.done();
            }
        }

        @Override
        public boolean belongsTo(Object family) {
            return SEARCH_JOB_FAMILY.equals(family);
        }
    }

    private final class RefreshTableJob extends UIJob {

        public static final String REFRESH_TABLE_JOB_FAMILY = "REFRESH_TABLE_JOB_FAMILY"; //$NON-NLS-1$

        private RefreshTableJob(String name) {
            super(name);
            setSystem(true);
        }

        @Override
        public IStatus runInUIThread(IProgressMonitor monitor) {
            try {
                if (!treeViewer.getControl().isDisposed()) {
                    if (initializeTableData) {
                        treeViewer.setInput(getViewSite());
                        initializeTableData = false;
                    } else {
                        refreshTable(monitor);
                    }
                }
                addSnippetAction.setEnabled(isImportSupported());
                if (monitor.isCanceled()) {
                    return Status.CANCEL_STATUS;
                } else {
                    return Status.OK_STATUS;
                }
            } finally {
                monitor.done();
            }
        }

        private void refreshTable(IProgressMonitor monitor) {
            int numberOfVisibleElements = treeViewer.getTree().getSize().y / treeViewer.getTree().getItemHeight()
                    + 1;
            try {
                monitor.beginTask(Messages.MONITOR_REFRESHING_TABLE, availableRepositories.size());
                treeViewer.refresh();
                int replacedElementsCount = 0;
                for (int i = 0; i < availableRepositories.size(); i++) {
                    SnippetRepositoryConfiguration config = availableRepositories.get(i);
                    List<KnownSnippet> elements = filteredSnippetsGroupedByRepoName.get(config);
                    treeViewer.setChildCount(config, elements.size());
                    for (int j = 0; j < elements.size() && replacedElementsCount < numberOfVisibleElements; j++) {
                        if (monitor.isCanceled()) {
                            return;
                        }
                        treeViewer.replace(config, j, elements.get(j));
                        replacedElementsCount++;
                    }
                    monitor.worked(1);
                }
            } finally {
                monitor.done();
            }
        }

        @Override
        public boolean belongsTo(Object family) {
            return REFRESH_TABLE_JOB_FAMILY.equals(family);
        }
    }

    public class KnownSnippet {
        public ISnippet snippet;
        public SnippetRepositoryConfiguration config;

        public KnownSnippet(SnippetRepositoryConfiguration config, ISnippet snippet) {
            this.config = config;
            this.snippet = snippet;
        }
    }

}