com.amazonaws.eclipse.dynamodb.testtool.TestToolVersionTable.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.dynamodb.testtool.TestToolVersionTable.java

Source

/*
 * Copyright 2013 Amazon Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *    http://aws.amazon.com/apache2.0
 *
 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and
 * limitations under the License.
 */
package com.amazonaws.eclipse.dynamodb.testtool;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

import com.amazonaws.eclipse.dynamodb.DynamoDBPlugin;
import com.amazonaws.eclipse.dynamodb.testtool.TestToolVersion.InstallState;

/**
 * A table that displays the set of available DynamoDB Local Test Tool
 * versions and allows you to install or uninstall them.
 */
public class TestToolVersionTable extends Composite {

    /**
     * Background job that checks for changes to the list of test tool
     * versions and updates the table.
     */
    private final Job updateJob = new Job("DynamoDB Local Test Tool Update") {
        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            setInput(TestToolManager.INSTANCE.getAllVersions());
            return Status.OK_STATUS;
        }
    };

    /**
     * Background job that installs a particular version of the test tool.
     */
    private class InstallJob extends Job {

        private final TestToolVersion version;

        /**
         * @param version The version of the test tool to install.
         */
        public InstallJob(final TestToolVersion version) {
            super("DynamoDB Local Test Tool Install");
            this.version = version;
        }

        /** {@inheritDoc} */
        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            // Mark the version as currently being installed so we disable
            // the 'install' button.
            TestToolManager.INSTANCE.markInstalling(version);
            checkForUpdates();

            // Actually do the install, updating the progress bar as we go.
            try {
                TestToolManager.INSTANCE.installVersion(version, progressMonitor);
            } finally {
                checkForUpdates();
            }

            return Status.OK_STATUS;
        }
    }

    /**
     * A background job that uninstalls a particular version of the test tool.
     */
    private class UninstallJob extends Job {
        private final TestToolVersion version;

        /**
         * @param version The version of the test tool to uninstall.
         */
        public UninstallJob(final TestToolVersion version) {
            super("DynamoDB Local Test Tool Uninstall");
            this.version = version;
        }

        /** {@inheritDoc} */
        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            TestToolManager.INSTANCE.uninstallVersion(version);
            checkForUpdates();
            return Status.OK_STATUS;
        }
    }

    private final Collection<Font> fonts = new LinkedList<Font>();

    private TreeViewer viewer;
    private ToolBarManager toolbar;
    private CustomProgressMonitor progressMonitor;

    private Group detailsGroup;
    private Label versionText;
    private Label descriptionText;

    private Action installAction;
    private Action uninstallAction;

    /**
     * @param parent The parent composite to attach to.
     */
    public TestToolVersionTable(final Composite parent) {
        super(parent, SWT.NONE);

        GridLayout layout = new GridLayout(1, false);
        setLayout(layout);

        createToolbar();
        createViewer();
        createDetailsGroup();

        createActions();
        hookToolbar();
        hookContextMenu();
        checkForUpdates();
    }

    /**
     * @return The currently selected test tool version (or null).
     */
    public TestToolVersion getSelection() {
        StructuredSelection selection = (StructuredSelection) viewer.getSelection();

        return (TestToolVersion) selection.getFirstElement();
    }

    /**
     * @return  an observable that tracks the current selection
     */
    public IObservableValue getObservableSelection() {
        return ViewersObservables.observeSingleSelection(viewer);
    }

    /** {@inheritDoc} */
    @Override
    public void dispose() {
        for (Font font : fonts) {
            font.dispose();
        }
        super.dispose();
    }

    /**
     * Create the toolbar, with install and uninstall buttons and an initially
     * hidden progress bar to track install status.
     */
    private void createToolbar() {

        // Bind to the top of wherever we end up.
        Composite wrapper = new Composite(this, SWT.NONE);
        wrapper.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        wrapper.setLayout(new GridLayout(2, false));

        toolbar = new CustomToolBarManager();

        // Toolbar on the left.
        Control control = toolbar.createControl(wrapper);
        control.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));

        // Progress bar on the right.
        ProgressBar progressBar = new ProgressBar(wrapper, SWT.NONE);
        progressBar.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));
        progressBar.setVisible(false);

        progressMonitor = new CustomProgressMonitor(progressBar);
    }

    /**
     * Create the actual table view displaying the list of selectable test
     * tool versions.
     */
    private void createViewer() {

        // Grab any leftover space in the middle.
        Composite wrapper = new Composite(this, SWT.NONE);
        GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
        data.heightHint = 200;
        wrapper.setLayoutData(data);

        TreeColumnLayout layout = new TreeColumnLayout();
        wrapper.setLayout(layout);

        viewer = new TreeViewer(wrapper,
                SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE);

        TestToolVersionProvider provider = new TestToolVersionProvider();
        viewer.setContentProvider(provider);
        viewer.setLabelProvider(provider);
        viewer.setComparator(new VersionComparator());
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(final SelectionChangedEvent event) {
                onSelectionChanged();
            }
        });

        Tree tree = viewer.getTree();
        tree.setHeaderVisible(true);

        TreeColumn versionColumn = new TreeColumn(tree, SWT.LEFT);
        versionColumn.setText("Version");
        layout.setColumnData(versionColumn, new ColumnWeightData(75));

        TreeColumn installColumn = new TreeColumn(tree, SWT.CENTER);
        installColumn.setText("Installed?");
        layout.setColumnData(installColumn, new ColumnWeightData(25));
    }

    /**
     * Create the (initially hidden) details group, which shows a longer text
     * description of the currently-selected version.
     */
    private void createDetailsGroup() {

        detailsGroup = new Group(this, SWT.NONE);
        detailsGroup.setText("Details");
        detailsGroup.setLayout(new GridLayout(2, false));
        detailsGroup.setVisible(false);

        // Pin to the bottom to allow the table control to have as much space
        // as it wants.
        GridData data = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
        data.widthHint = 300;
        detailsGroup.setLayoutData(data);

        Label version = new Label(detailsGroup, SWT.TOP);
        version.setText("Version: ");
        makeBold(version);

        versionText = new Label(detailsGroup, SWT.WRAP);

        Label description = new Label(detailsGroup, SWT.TOP);
        description.setText("Description: ");
        makeBold(description);

        descriptionText = new Label(detailsGroup, SWT.WRAP);

        data = new GridData(SWT.FILL, SWT.FILL, true, true);
        data.widthHint = 300;
        descriptionText.setLayoutData(data);
    }

    /**
     * Make the given label bold.
     *
     * @param label The label to embolden.
     */
    private void makeBold(final Label label) {
        FontData[] fontData = label.getFont().getFontData();
        fontData[0].setStyle(SWT.BOLD);

        Font font = new Font(getDisplay(), fontData[0]);
        fonts.add(font);

        label.setFont(font);
    }

    /**
     * Create the install and uninstall actions for the toolbar and context
     * menu.
     */
    private void createActions() {
        installAction = new Action() {
            @Override
            public void run() {
                new InstallJob(getSelection()).schedule();
            }
        };
        installAction.setText("Install");
        installAction.setToolTipText("Install the selected version.");
        installAction.setImageDescriptor(DynamoDBPlugin.getDefault().getImageRegistry().getDescriptor("add"));
        installAction.setEnabled(false);

        uninstallAction = new Action() {
            @Override
            public void run() {
                new UninstallJob(getSelection()).schedule();
            }
        };
        uninstallAction.setText("Uninstall");
        uninstallAction.setToolTipText("Uninstall the selected version.");
        uninstallAction.setImageDescriptor(DynamoDBPlugin.getDefault().getImageRegistry().getDescriptor("remove"));
        uninstallAction.setEnabled(false);
    }

    /**
     * Hook the actions created above into the toolbar.
     */
    private void hookToolbar() {
        toolbar.add(installAction);
        toolbar.add(new Separator());
        toolbar.add(uninstallAction);
        toolbar.update(true);
    }

    /**
     * Hook the actions created above into the context menu for the table.
     */
    private void hookContextMenu() {
        MenuManager manager = new MenuManager("#PopupMenu");
        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                manager.add(installAction);
                manager.add(uninstallAction);
            }
        });

        Menu menu = manager.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);

        manager.createContextMenu(viewer.getControl());
    }

    /**
     * Check for changes to the list of test tool versions.
     */
    private void checkForUpdates() {
        updateJob.schedule();
    }

    /**
     * Handler callback for when the selected version changes. Enable or
     * disable the install/uninstall actions as appropriate, and update the
     * details group.
     */
    private void onSelectionChanged() {
        TestToolVersion version = getSelection();
        if (version == null) {
            installAction.setEnabled(false);
            uninstallAction.setEnabled(false);
            detailsGroup.setVisible(false);
        } else {
            installAction.setEnabled(version.getInstallState() == InstallState.NOT_INSTALLED);
            uninstallAction.setEnabled(version.getInstallState() == InstallState.INSTALLED);

            versionText.setText(version.getName());
            descriptionText.setText(version.getDescription());
            detailsGroup.setVisible(true);
            detailsGroup.pack();
        }
        toolbar.update(false);
    }

    /**
     * Set the list of test tool versions to display in the table.
     *
     * @param versions The list of versions.
     */
    private void setInput(final List<TestToolVersion> versions) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                _setInput(versions);
            }
        });
    }

    /**
     * Helper for setInput() that MUST be run on the UI thread.
     */
    private void _setInput(final List<TestToolVersion> versions) {
        TestToolVersion previous = getSelection();

        viewer.setInput(versions);

        if (previous != null) {
            Tree tree = viewer.getTree();

            for (int i = 0; i < tree.getItemCount(); ++i) {
                TreeItem item = tree.getItem(i);
                TestToolVersion version = (TestToolVersion) item.getData();

                if (previous.getName().equals(version.getName())) {
                    tree.select(item);
                    tree.notifyListeners(SWT.Selection, null);
                    break;
                }
            }
        }
    }

    /**
     * A content and label provider for the test tool table.
     */
    private class TestToolVersionProvider extends LabelProvider
            implements ITreeContentProvider, ITableLabelProvider {

        private List<TestToolVersion> versions;

        /** {@inheritDoc} */
        public Object[] getElements(final Object inputElement) {
            if (versions == null) {
                return null;
            }
            return versions.toArray();
        }

        /** {@inheritDoc} */
        @SuppressWarnings("unchecked")
        public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {

            versions = (List<TestToolVersion>) newInput;
        }

        /** {@inheritDoc} */
        public Image getColumnImage(final Object element, final int columnIndex) {

            return null;
        }

        /** {@inheritDoc} */
        public String getColumnText(final Object element, final int columnIndex) {

            if (!(element instanceof TestToolVersion)) {
                return "???";
            }

            TestToolVersion version = (TestToolVersion) element;

            switch (columnIndex) {
            case 0:
                return version.getName();

            case 1:
                if (version.isRunning()) {
                    return "Running...";
                }
                if (version.isInstalled()) {
                    return "Yes";
                }
                if (version.isInstalling()) {
                    return "Installing...";
                }
                return "";

            default:
                return "???";
            }
        }

        /** {@inheritDoc} */
        public Object[] getChildren(final Object parentElement) {
            return new Object[0];
        }

        /** {@inheritDoc} */
        public Object getParent(final Object element) {
            return null;
        }

        /** {@inheritDoc} */
        public boolean hasChildren(final Object element) {
            return false;
        }
    }

    /**
     * Sort versions in reverse alphabetical order (which, since version
     * numbers are dates, is also conveniently reverse chronological order).
     */
    private class VersionComparator extends ViewerComparator {
        /** {@inheritDoc} */
        @Override
        public int compare(final Viewer viewer, final Object e1, final Object e2) {

            if (!(e1 instanceof TestToolVersion)) {
                return 0;
            }
            if (!(e2 instanceof TestToolVersion)) {
                return 0;
            }

            TestToolVersion v1 = (TestToolVersion) e1;
            TestToolVersion v2 = (TestToolVersion) e2;

            return v2.getName().compareTo(v1.getName());
        }
    }

    /**
     * A Progress Monitor that updates a ProgressBar as the test tool is
     * installed.
     */
    private static class CustomProgressMonitor implements IProgressMonitor {

        private final ProgressBar progressBar;

        /**
         * @param progressBar The progress bar to update.
         */
        public CustomProgressMonitor(final ProgressBar progressBar) {
            this.progressBar = progressBar;
        }

        /** {@inheritDoc} */
        public void beginTask(final String name, final int totalWork) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    progressBar.setMinimum(0);
                    progressBar.setMaximum(totalWork);
                    progressBar.setSelection(0);
                    progressBar.setToolTipText(name);
                    progressBar.setVisible(true);
                }
            });
        }

        /** {@inheritDoc} */
        public void worked(final int work) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    progressBar.setSelection(progressBar.getSelection() + work);
                }
            });
        }

        /** {@inheritDoc} */
        public void done() {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    progressBar.setVisible(false);
                }
            });
        }

        /** {@inheritDoc} */
        public void internalWorked(double arg0) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public boolean isCanceled() {
            return false;
        }

        /** {@inheritDoc} */
        public void setCanceled(boolean arg0) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public void setTaskName(String arg0) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public void subTask(String arg0) {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * A toolbar manager that forces text mode on all actions added to it.
     */
    private static class CustomToolBarManager extends ToolBarManager {
        public CustomToolBarManager() {
            super(SWT.FLAT | SWT.RIGHT);
        }

        /** {@inheritDoc} */
        @Override
        public void add(final IAction action) {
            ActionContributionItem item = new ActionContributionItem(action);
            item.setMode(ActionContributionItem.MODE_FORCE_TEXT);
            super.add(item);
        }
    }

}