org.eclipse.wb.tests.gef.UiContext.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.tests.gef.UiContext.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Google, Inc.
 * 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:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.tests.gef;

import com.google.common.base.Predicate;
import com.google.common.collect.Lists;

import org.eclipse.wb.internal.core.utils.check.Assert;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;

import junit.framework.AssertionFailedError;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.NestableException;

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

/**
 * Helper for testing SWT UI.
 * 
 * @author scheglov_ke
 */
public class UiContext {
    private final Display m_display;
    private Shell m_shell;
    private final LinkedList<Shell> m_shells = Lists.newLinkedList();

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////////
    public UiContext() {
        m_display = Display.getCurrent();
    }

    public UiContext(Display display) {
        m_display = display;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Access
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return {@link Shell} with given text, may be <code>null</code>.
     */
    public Shell getShell(String text) {
        for (Shell shell : m_display.getShells()) {
            if (text.equals(shell.getText())) {
                return shell;
            }
        }
        return null;
    }

    /**
     * Specifies that {@link Shell} with given text should be used to search {@link Widget}'s.
     */
    public Shell useShell(String text) {
        // remember current Shell
        m_shells.addFirst(getShell());
        // set new Shell
        for (Shell shell : m_display.getShells()) {
            if (text.equals(shell.getText())) {
                m_shell = shell;
                return m_shell;
            }
        }
        throw new IllegalArgumentException("Unable to find Shell: " + text);
    }

    /**
     * Specifies that it is expected that current {@link Shell} was closed, so we return to previous.
     */
    public void popShell() {
        m_shell = m_shells.removeFirst();
    }

    /**
     * @return the {@link Shell} to use as start to searching {@link Widget}'s.
     */
    private Shell getShell() {
        if (m_shell != null) {
            if (m_shell.isDisposed()) {
                m_shell = null;
            } else {
                return m_shell;
            }
        }
        return getActiveShell();
    }

    /**
     * @return the active {@link Shell}.
     */
    public Shell getActiveShell() {
        return m_display.getActiveShell();
    }

    /**
     * @return the first {@link Widget} with compatible {@link Class}.
     */
    public <T extends Widget> T findFirstWidget(Class<T> classToFind) {
        return findFirstWidget(getShell(), classToFind);
    }

    /**
     * @return the first {@link Widget} with compatible {@link Class}.
     */
    public static <T extends Widget> T findFirstWidget(Widget start, Class<T> classToFind) {
        List<T> widgets = findWidgets(start, classToFind);
        if (!widgets.isEmpty()) {
            return widgets.get(0);
        }
        return null;
    }

    /**
     * @return the {@link List} of {@link Widget}'s with compatible {@link Class}.
     */
    public <T extends Widget> List<T> findWidgets(final Class<T> classToFind) {
        Shell shell = getShell();
        return findWidgets(shell, classToFind);
    }

    /**
     * @return the {@link List} of {@link Widget}'s with compatible {@link Class}.
     */
    public static <T extends Widget> List<T> findWidgets(Widget start, final Class<T> classToFind) {
        final List<T> widgets = Lists.newArrayList();
        visit(start, new IWidgetsVisitor() {
            @Override
            @SuppressWarnings("unchecked")
            public void endVisit(Widget widget) {
                if (classToFind.isAssignableFrom(widget.getClass())) {
                    widgets.add((T) widget);
                }
            }
        });
        //
        return widgets;
    }

    /**
     * @return the {@link Button} with given text.
     */
    public Button getButtonByText(String text) {
        return getButtonByText(getShell(), text);
    }

    /**
     * @return the {@link Button} with given text.
     */
    public Button getButtonByTextPrefix(final String prefix) {
        return getButton(getShell(), new Predicate<String>() {
            public boolean apply(String input) {
                return input != null && input.startsWith(prefix);
            }
        });
    }

    /**
     * @return the {@link Button} with given text.
     */
    public Button getButtonByText(Widget start, final String text) {
        return getButton(start, new Predicate<String>() {
            public boolean apply(String input) {
                return isSameText(input, text);
            }
        });
    }

    /**
     * @return the {@link Button} with given text.
     */
    public Button getButton(Widget start, final Predicate<String> predicate) {
        final Button[] result = new Button[1];
        visit(start, new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof Button) {
                    Button button = (Button) widget;
                    if (predicate.apply(button.getText()) || predicate.apply(button.getToolTipText())) {
                        result[0] = button;
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link ToolItem} with given text.
     */
    public ToolItem getToolItem(final String text) {
        final ToolItem[] result = new ToolItem[1];
        visit(getShell(), new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof ToolItem) {
                    ToolItem item = (ToolItem) widget;
                    if (text.equals(item.getText()) || text.equals(item.getToolTipText())) {
                        result[0] = item;
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link TreeItem} with given text.
     */
    public TreeItem getTreeItem(final String text) {
        final TreeItem[] result = new TreeItem[1];
        visit(getShell(), new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof TreeItem) {
                    TreeItem item = (TreeItem) widget;
                    if (text.equals(item.getText())) {
                        result[0] = item;
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link TabItem} with given text.
     */
    public TabItem getTabItem(final String text) {
        final TabItem[] result = new TabItem[1];
        visit(getShell(), new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof TabItem) {
                    TabItem item = (TabItem) widget;
                    if (text.equals(item.getText())) {
                        result[0] = item;
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link Text} widget that has {@link Label} with given text.
     */
    public Text getTextByLabel(final String labelText) {
        final Text[] result = new Text[1];
        visit(getShell(), new IWidgetsVisitor() {
            private boolean m_labelFound;

            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof Label) {
                    Label label = (Label) widget;
                    m_labelFound = isSameText(label.getText(), labelText);
                }
                if (widget instanceof Text && m_labelFound) {
                    result[0] = (Text) widget;
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link Text} widget that has given text.
     */
    public Text getTextByText(final String textText) {
        final Text[] result = new Text[1];
        visit(getShell(), new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof Text) {
                    Text text = (Text) widget;
                    if (text.getText().equals(textText)) {
                        result[0] = (Text) widget;
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link Widget}.
     */
    public void click(Widget widget) {
        widget.notifyListeners(SWT.Selection, new Event());
    }

    /**
     * Sends {@link SWT#Selection} event to the {@link Button} with given text.
     */
    public void clickButton(String text) {
        Button button = getButtonByText(text);
        Assert.isNotNull(button, "Can not find button with text |" + text + "|");
        clickButton(button);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link Button}.
     */
    public void clickButton(Button button) {
        click(button);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link Button}.
     */
    public void selectButton(String text, boolean selection) {
        Button button = getButtonByText(text);
        Assert.isNotNull(button, "Can not find button with text |" + text + "|");
        button.setSelection(selection);
        clickButton(button);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link Button}.
     */
    private void selectButton(Button button, boolean selection) {
        button.setSelection(selection);
        clickButton(button);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link Button}.
     */
    public void selectButton(Button button) {
        // if Button in RADIO, deselect all other RADIO Button's
        if ((button.getStyle() & SWT.RADIO) != 0) {
            Control[] children = button.getParent().getChildren();
            for (int i = 0; i < children.length; i++) {
                Control child = children[i];
                if (child instanceof Button && (child.getStyle() & SWT.RADIO) != 0) {
                    Button childButton = (Button) child;
                    if (childButton != button) {
                        selectButton(childButton, false);
                    }
                }
            }
        }
        // select needed Button
        button.setFocus();
        selectButton(button, true);
    }

    /**
     * Selects {@link Button} with given text.
     */
    public void selectButton(String text) {
        Button button = getButtonByText(text);
        Assert.isNotNull(button, "Can not find button with text |" + text + "|");
        selectButton(button);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link ToolItem} with detail.
     */
    public void click(ToolItem toolItem, int detail) {
        Event event = new Event();
        event.detail = detail;
        toolItem.notifyListeners(SWT.Selection, event);
    }

    /**
     * Sends {@link SWT#Selection} event to given {@link MenuItem} as radio item.
     */
    public void selectMenuItem(MenuItem menuItem, boolean selection) {
        menuItem.setSelection(selection);
        click(menuItem);
    }

    public void selectMenuItem(MenuItem menuItem) {
        selectMenuItem(menuItem, true);
    }

    /**
     * @return the {@link Control} located in children on its {@link Composite} after {@link Label}
     *         with given text.
     */
    public Control getControlAfterLabel(String text) {
        List<Label> labels = findWidgets(Label.class);
        for (Label label : labels) {
            String labelText = label.getText().trim();
            if (isSameText(labelText, text)) {
                Control[] children = label.getParent().getChildren();
                // get next Control
                int index = ArrayUtils.indexOf(children, label);
                if (index < children.length - 1) {
                    return children[index + 1];
                }
                // stop it any case
                break;
            }
        }
        // not found
        return null;
    }

    /**
     * @return the {@link Control} located in children on its {@link Composite} after given one.
     */
    @SuppressWarnings("unchecked")
    public <T> T getControlAfter(Control reference) {
        Control[] children = reference.getParent().getChildren();
        // get next Control
        int index = ArrayUtils.indexOf(children, reference);
        if (index < children.length - 1) {
            return (T) children[index + 1];
        }
        // not found
        return null;
    }

    /**
     * Sets given {@link TreeItem} (and only it) expanded in its {@link Tree}.
     */
    public static void setExpanded(TreeItem treeItem, boolean expanded) {
        treeItem.setExpanded(expanded);
        Event event = new Event();
        event.widget = treeItem.getParent();
        event.item = treeItem;
        treeItem.getParent().notifyListeners(SWT.Expand, event);
    }

    /**
     * Sets given {@link TreeItem} (and only it) selected in its {@link Tree}.
     */
    public static void setSelection(TreeItem treeItem) {
        Tree tree = treeItem.getParent();
        tree.setSelection(treeItem);
        treeItem.getParent().notifyListeners(SWT.Selection, null);
    }

    /**
     * Selects items in {@link org.eclipse.swt.widgets.List}.
     */
    public static void setSelection(org.eclipse.swt.widgets.List list, String item) {
        list.setSelection(new String[] { item });
        list.notifyListeners(SWT.Selection, null);
    }

    public static void setChecked(TreeItem treeItem, boolean checked) {
        treeItem.setChecked(checked);
        // notify about check
        Event event = new Event();
        event.detail = SWT.CHECK;
        event.item = treeItem;
        treeItem.getParent().notifyListeners(SWT.Selection, event);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Menu
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link Menu} widget that active for current {@link Shell}.
     */
    public Menu getActiveMenu() {
        return (Menu) ReflectionUtils.getFieldObject(getShell(), "activeMenu");
    }

    /**
     * @return the {@link Menu} widget that active for current {@link Display}.
     */
    public Menu getLastPopup() {
        Menu menu = null;
        Menu[] popups = (Menu[]) ReflectionUtils.getFieldObject(m_display, "popups");
        for (int i = 0; i < popups.length; i++) {
            if (popups[i] != null) {
                menu = popups[i];
            }
        }
        return menu;
    }

    /**
     * @return the {@link MenuItem} with given text.
     */
    public MenuItem getMenuItem(Menu menu, final String text) {
        final MenuItem[] result = new MenuItem[1];
        visit(menu, new IWidgetsVisitor() {
            @Override
            public void endVisit(Widget widget) {
                if (widget instanceof MenuItem) {
                    MenuItem item = (MenuItem) widget;
                    if (isSameText(item.getText(), text)) {
                        result[0] = item;
                    }
                }
            }
        });
        return result[0];
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Text
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return <code>true</code> if given {@link String}s have same content. Special characters are
     *         ignored.
     */
    private static boolean isSameText(String s1, String s2) {
        if (s1 == null || s2 == null) {
            return false;
        }
        s1 = normalizeTextForComparing(s1);
        s2 = normalizeTextForComparing(s2);
        return s1.equals(s2);
    }

    /**
     * Normalizes given {@link String} by removing special characters.
     */
    private static String normalizeTextForComparing(String s) {
        s = s.trim();
        s = StringUtils.remove(s, '&');
        s = StringUtils.substringBefore(s, "\t");
        return s;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Visiting
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Visitor for all {@link Widget}'s.
     */
    public static class IWidgetsVisitor {
        /**
         * @return <code>true</code> if the children of this {@link Widget} should be visited, and
         *         <code>false</code> if the children of this {@link Widget} should be skipped.
         */
        public boolean visit(Widget widget) {
            return true;
        }

        /**
         * Invoked after visiting all children of this {@link Control}.
         */
        public void endVisit(Widget widget) {
        }
    }

    /**
     * Visits all {@link Widgets}'s starting from given one.
     */
    public static void visit(Widget widget, IWidgetsVisitor visitor) {
        // ignore invisible Control's
        if (widget instanceof Control) {
            Control control = (Control) widget;
            if (control.getParent() != null && control.getParent().getLayout() instanceof StackLayout
                    && !control.isVisible()) {
                return;
            }
        }
        // visit
        if (visitor.visit(widget)) {
            // Composite
            if (widget instanceof Composite) {
                Composite composite = (Composite) widget;
                Control[] children = composite.getChildren();
                for (int i = 0; i < children.length; i++) {
                    Control child = children[i];
                    visit(child, visitor);
                }
            }
            // ToolBar
            if (widget instanceof ToolBar) {
                ToolBar toolBar = (ToolBar) widget;
                for (ToolItem toolItem : toolBar.getItems()) {
                    visit(toolItem, visitor);
                }
            }
            // TabFolder
            if (widget instanceof TabFolder) {
                TabFolder tabFolder = (TabFolder) widget;
                for (TabItem tabItem : tabFolder.getItems()) {
                    visit(tabItem, visitor);
                }
            }
            // TabItem
            if (widget instanceof TabItem) {
                TabItem tabItem = (TabItem) widget;
                Control control = tabItem.getControl();
                if (control != null) {
                    visit(control, visitor);
                }
            }
            // Tree
            if (widget instanceof Tree) {
                Tree tree = (Tree) widget;
                for (TreeItem treeItem : tree.getItems()) {
                    visit(treeItem, visitor);
                }
            }
            if (widget instanceof TreeItem) {
                TreeItem parent = (TreeItem) widget;
                for (TreeItem treeItem : parent.getItems()) {
                    visit(treeItem, visitor);
                }
            }
            // Menu
            if (widget instanceof Menu) {
                Menu menu = (Menu) widget;
                for (MenuItem menuItem : menu.getItems()) {
                    visit(menuItem, visitor);
                }
            }
            if (widget instanceof MenuItem) {
                MenuItem menuItem = (MenuItem) widget;
                visit(menuItem.getMenu(), visitor);
            }
            // end
            visitor.endVisit(widget);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Execution
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Executes one {@link UIRunnable} (can block UI) and uses other {@link UIRunnable} to check
     * result.
     */
    public void executeAndCheck(UIRunnable uiRunnable, final UIRunnable checkRunnable) throws Exception {
        // run checkRunnable
        final Throwable[] checkException = new Throwable[1];
        Thread checkThread = new Thread("UIContext_checkThread") {
            @Override
            public void run() {
                m_display.asyncExec(new Runnable() {
                    public void run() {
                        try {
                            checkRunnable.run(UiContext.this);
                        } catch (Throwable e) {
                            e.printStackTrace();
                            checkException[0] = e;
                        }
                    }
                });
            }
        };
        checkThread.start();
        // now run uiRunnable, it can block
        uiRunnable.run(this);
        // wait also for 'checkThread'
        checkThread.join();
        // check for exception
        if (checkException[0] != null) {
            if (checkException[0] instanceof AssertionFailedError) {
                AssertionFailedError error = (AssertionFailedError) checkException[0];
                throw error;
            }
            throw new NestableException("Exception during running 'check' UIRunnable.", checkException[0]);
        }
    }

    /**
     * Waits until given condition will be satisfied.
     */
    public void waitFor(UIPredicate predicate) throws InterruptedException {
        while (!predicate.check()) {
            waitEventLoop(10);
        }
    }

    /**
     * Waits given number of milliseconds and runs events loop every 1 millisecond.
     */
    private void waitEventLoop(int time) throws InterruptedException {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < time) {
            Thread.sleep(0);
            while (m_display.readAndDispatch()) {
                // do nothing
            }
        }
    }
}