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

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.tests.gef.GraphicalRobot.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.ImmutableList;

import org.eclipse.wb.draw2d.Figure;
import org.eclipse.wb.draw2d.FigureUtils;
import org.eclipse.wb.draw2d.IPositionConstants;
import org.eclipse.wb.draw2d.Layer;
import org.eclipse.wb.draw2d.Polyline;
import org.eclipse.wb.draw2d.geometry.Dimension;
import org.eclipse.wb.draw2d.geometry.Point;
import org.eclipse.wb.draw2d.geometry.PointList;
import org.eclipse.wb.draw2d.geometry.Rectangle;
import org.eclipse.wb.gef.core.Command;
import org.eclipse.wb.gef.core.EditPart;
import org.eclipse.wb.gef.core.IEditPartViewer;
import org.eclipse.wb.gef.core.tools.Tool;
import org.eclipse.wb.gef.graphical.GraphicalEditPart;
import org.eclipse.wb.gef.graphical.handles.Handle;
import org.eclipse.wb.gef.graphical.handles.MoveHandle;
import org.eclipse.wb.gef.graphical.handles.ResizeHandle;
import org.eclipse.wb.gef.graphical.tools.ResizeTracker;
import org.eclipse.wb.gef.graphical.tools.SelectionTool;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.gef.graphical.GraphicalViewer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Text;

import junit.framework.Assert;

import static org.fest.assertions.Assertions.assertThat;

import org.apache.commons.lang.ObjectUtils;
import org.fest.assertions.Description;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

/**
 * Fluent interface for performing create/move/resize operations on {@link GraphicalViewer}.
 * 
 * @author scheglov_ke
 */
public final class GraphicalRobot {
    // viewer
    private final GraphicalViewer m_viewer;
    private final EventSender m_sender;
    // source
    private boolean sourceSideMode = false;
    private int sourceWidth;
    private int sourceHeight;
    private int mouseInSourceX;
    private int mouseInSourceY;
    // side
    private boolean leftSide = true;
    private boolean topSide = true;
    // target
    private GraphicalEditPart target;
    private Rectangle targetBounds;
    // mouse
    private int mouseX;
    private int mouseY;
    private int mouseOldX;
    private int mouseOldY;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public GraphicalRobot(GraphicalViewer viewer) {
        m_viewer = viewer;
        m_sender = new EventSender(viewer.getControl());
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Emulation: state mask
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Emulates CTRL key down.
     */
    public GraphicalRobot ctrlDown() {
        m_sender.ctrlDown();
        return this;
    }

    /**
     * Emulates CTRL key up.
     */
    public GraphicalRobot ctrlUp() {
        m_sender.ctrlUp();
        return this;
    }

    /**
     * Sends {@link SWT#KeyDown} event.
     */
    public void keyDown(int key) {
        m_sender.keyDown(key);
    }

    /**
     * Sends {@link SWT#KeyDown} event.
     */
    public void keyDown(int key, char c) {
        m_sender.keyDown(key, c);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Selection
    //
    ////////////////////////////////////////////////////////////////////////////
    public void deselectAll() {
        m_viewer.deselectAll();
    }

    /**
     * Selects {@link EditPart}'s of given models (and only them).
     */
    public void select(Object... models) {
        EditPart[] editParts = getEditParts(models);
        m_viewer.setSelection(ImmutableList.copyOf(editParts));
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Operation selection
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Informs that CREATE operation is in progress. Source object has no size.
     */
    public GraphicalRobot create() {
        create(0, 0);
        return this;
    }

    /**
     * Informs that CREATE operation is in progress. Mouse cursor is in center of source.
     */
    public GraphicalRobot create(int width, int height) {
        create(width, height, width / 2, height / 2);
        return this;
    }

    /**
     * Informs that CREATE operation is in progress.
     */
    private void create(int width, int height, int mouseInSourceX_, int mouseInSourceY_) {
        sourceWidth = width;
        sourceHeight = height;
        mouseInSourceX = mouseInSourceX_;
        mouseInSourceY = mouseInSourceY_;
    }

    /**
     * Informs that MOVE should be performed.
     */
    public GraphicalRobot beginMove(Object object) {
        GraphicalEditPart editPart = getEditPart(object);
        Rectangle bounds = getAbsoluteBounds(editPart);
        sourceWidth = bounds.width;
        sourceHeight = bounds.height;
        m_viewer.select(editPart);
        // find MoveHandle
        mouseX = bounds.x;
        mouseY = bounds.y;
        while (!(m_viewer.findTargetHandle(mouseX, mouseY) instanceof MoveHandle)) {
            mouseX++;
        }
        mouseInSourceX = mouseX - bounds.x;
        mouseInSourceY = mouseY - bounds.y;
        // begin drag
        return beginDrag();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Operation: resize and other SquareHandle-s
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Prepares mouse location on {@link ResizeHandle} with given direction.
     */
    public GraphicalRobot toResizeHandle(Object object, final int direction) {
        Predicate<Handle> predicate = new Predicate<Handle>() {
            public boolean apply(Handle handle) {
                if (handle instanceof ResizeHandle) {
                    ResizeHandle resizeHandle = (ResizeHandle) handle;
                    return resizeHandle.getDirection() == direction;
                }
                return false;
            }
        };
        toHandle(object, predicate);
        // continue
        return this;
    }

    /**
     * Prepares mouse location on resize {@link Handle} with given type and direction.
     */
    public GraphicalRobot toResizeHandle(Object object, final Object type, final int direction) {
        Predicate<Handle> predicate = new Predicate<Handle>() {
            public boolean apply(Handle handle) {
                if (handle.getDragTrackerTool() instanceof ResizeTracker) {
                    ResizeTracker resizeTracker = (ResizeTracker) handle.getDragTrackerTool();
                    return resizeTracker.getDirection() == direction
                            && ObjectUtils.equals(resizeTracker.getRequestType(), type);
                }
                return false;
            }
        };
        toHandle(object, predicate);
        // continue
        return this;
    }

    /**
     * Informs that RESIZE should be performed, using standard {@link ResizeHandle}.
     */
    public GraphicalRobot beginResize(Object object, int direction) {
        toResizeHandle(object, direction);
        beginDrag();
        return this;
    }

    /**
     * Prepares mouse location on side {@link Handle} that satisfies predicate.
     */
    private GraphicalRobot toHandle(Object object, Predicate<Handle> predicate) {
        GraphicalEditPart editPart = getEditPart(object);
        Rectangle bounds = getAbsoluteBounds(editPart);
        sourceWidth = bounds.width;
        sourceHeight = bounds.height;
        m_viewer.select(editPart);
        // find Handle
        Point location = findSideHandle(bounds, predicate);
        assertThat(location).describedAs("Side Handle for " + predicate).isNotNull();
        mouseX = location.x;
        mouseY = location.y;
        mouseInSourceX = mouseX - bounds.x;
        mouseInSourceY = mouseY - bounds.y;
        // continue
        return this;
    }

    private Point findSideHandle(Rectangle bounds, Predicate<Handle> predicate) {
        Point location = null;
        {
            location = findSideHandle(predicate, bounds, 0, 0, 1, 0);
        }
        if (location == null) {
            location = findSideHandle(predicate, bounds, 0, 0, 0, 1);
        }
        if (location == null) {
            location = findSideHandle(predicate, bounds, bounds.width - 1, 0, 0, 1);
            if (location != null) {
                location.translate(1, 0);
                location.translate(0, 1);
            }
        }
        if (location == null) {
            location = findSideHandle(predicate, bounds, 0, bounds.height - 1, 1, 0);
            if (location != null) {
                location.translate(1, 0);
                location.translate(0, 1);
            }
        }
        return location;
    }

    private Point findSideHandle(Predicate<Handle> predicate, Rectangle bounds, int x, int y, int deltaX,
            int deltaY) {
        x += bounds.x;
        y += bounds.y;
        while (x < bounds.right() && y < bounds.bottom()) {
            Handle handle = m_viewer.findTargetHandle(x, y);
            if (predicate.apply(handle)) {
                return handle.getBounds().getCenter();
            }
            x += deltaX;
            y += deltaY;
        }
        return null;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Side to set position
    //
    ////////////////////////////////////////////////////////////////////////////
    public GraphicalRobot leftSide() {
        leftSide = true;
        return this;
    }

    public GraphicalRobot rightSide() {
        leftSide = false;
        return this;
    }

    public GraphicalRobot topSide() {
        topSide = true;
        return this;
    }

    public GraphicalRobot bottomSide() {
        topSide = false;
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Mouse in source
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Specifies that we want to control directly mouse location, not location of source side. Use
     * {@link #sideMode()} to use "side" mode.
     */
    public GraphicalRobot mouseMode() {
        sourceSideMode = false;
        return this;
    }

    /**
     * Specifies that you want to set location of sides, see {@link #leftSide()}, {@link #rightSide()}
     * , etc.
     */
    public GraphicalRobot sideMode() {
        sourceSideMode = true;
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Target
    //
    ////////////////////////////////////////////////////////////////////////////
    public GraphicalRobot target(Object object) {
        target = getEditPart(object);
        targetBounds = getAbsoluteBounds(target);
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Position
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Location of source side in target, if positive - from left/top, if negative - from
     * right/bottom.
     */
    public GraphicalRobot in(int x, int y) {
        inX(x);
        inY(y);
        return this;
    }

    /**
     * Location of source side in target, if positive - from left, if negative - from right.
     */
    public GraphicalRobot inX(int location) {
        int sideInTarget = normalizeIn(location, targetBounds.width);
        int mouseInTarget = sideToMouseX(sideInTarget);
        mouseX = targetToAbsoluteX(mouseInTarget);
        return this;
    }

    /**
     * Location of source side in target, if positive - from top, if negative - from bottom.
     */
    public GraphicalRobot inY(int location) {
        int sideInTarget = normalizeIn(location, targetBounds.height);
        int mouseInTarget = sideToMouseY(sideInTarget);
        mouseY = targetToAbsoluteY(mouseInTarget);
        return this;
    }

    /**
     * Distance of source side from target, if negative - from left/top, if positive - from
     * right/bottom.
     */
    public GraphicalRobot out(int x, int y) {
        outX(x);
        outY(y);
        return this;
    }

    /**
     * Distance of source side from target, if negative - from left, if positive - from right.
     */
    public GraphicalRobot outX(int location) {
        int sideRelTarget = normalizeOut(location, targetBounds.width);
        int mouseRelTarget = sideToMouseX(sideRelTarget);
        mouseX = targetToAbsoluteX(mouseRelTarget);
        return this;
    }

    /**
     * Distance of source side from target, if negative - from top, if positive - from bottom.
     */
    public GraphicalRobot outY(int location) {
        int sideRelTarget = normalizeOut(location, targetBounds.height);
        int mouseRelTarget = sideToMouseY(sideRelTarget);
        mouseY = targetToAbsoluteY(mouseRelTarget);
        return this;
    }

    /**
     * Location in target, in fractions of its width/height.
     */
    public GraphicalRobot in(double x, double y) {
        inX(x);
        inY(y);
        return this;
    }

    /**
     * Location in target, in fractions of its width if <code>abs() less 1.0</code>, or in pixels as
     * {@link #inX(int)}.
     */
    public GraphicalRobot inX(double k) {
        if (k > -1.0 && k < 1.0) {
            int location = (int) (targetBounds.width * k);
            int sideInTarget = normalizeIn(location, targetBounds.width);
            int mouseInTarget = sideToMouseX(sideInTarget);
            mouseX = targetToAbsoluteX(mouseInTarget);
        } else {
            inX((int) k);
        }
        return this;
    }

    /**
     * Location in target, in fractions of its width if <code>abs() less 1.0</code>, or in pixels as
     * {@link #inY(int)}.
     */
    public GraphicalRobot inY(double k) {
        if (k > -1.0 && k < 1.0) {
            int location = (int) (targetBounds.height * k);
            int sideInTarget = normalizeIn(location, targetBounds.height);
            int mouseInTarget = sideToMouseY(sideInTarget);
            mouseY = targetToAbsoluteY(mouseInTarget);
        } else {
            inY((int) k);
        }
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Location utils
    //
    ////////////////////////////////////////////////////////////////////////////
    private static int normalizeIn(int location, int targetSize) {
        if (location < 0) {
            location += targetSize;
        }
        return location;
    }

    private static int normalizeOut(int location, int targetSize) {
        if (location > 0) {
            location += targetSize;
        }
        return location;
    }

    private int sideToMouseX(int side) {
        if (!sourceSideMode) {
            return side;
        }
        if (leftSide) {
            return side + mouseInSourceX;
        } else {
            return side - (sourceWidth - mouseInSourceX);
        }
    }

    private int sideToMouseY(int side) {
        if (!sourceSideMode) {
            return side;
        }
        if (topSide) {
            return side + mouseInSourceY;
        } else {
            return side - (sourceHeight - mouseInSourceY);
        }
    }

    private int targetToAbsoluteX(int x) {
        return targetBounds.x + x;
    }

    private int targetToAbsoluteY(int y) {
        return targetBounds.y + y;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Operations
    //
    ////////////////////////////////////////////////////////////////////////////
    public GraphicalRobot moveTo(int _mouseX, int _mouseY) {
        mouseX = _mouseX;
        mouseY = _mouseY;
        return move();
    }

    public GraphicalRobot moveTo(Object object, int _mouseX, int _mouseY) {
        target(object);
        in(_mouseX, _mouseY);
        return move();
    }

    public GraphicalRobot moveTo(Object object, double _mouseX, double _mouseY) {
        target(object);
        inX(_mouseX);
        inY(_mouseY);
        return move();
    }

    public GraphicalRobot moveTo(Object object) {
        return moveTo(object, 0, 0);
    }

    public GraphicalRobot moveOn(int deltaX, int deltaY) {
        mouseX += deltaX;
        mouseY += deltaY;
        return move();
    }

    public GraphicalRobot move() {
        m_sender.moveTo(mouseX, mouseY);
        return this;
    }

    public GraphicalRobot beginDrag(int button) {
        m_sender.startDrag(mouseX, mouseY, button);
        mouseOldX = mouseX;
        mouseOldY = mouseY;
        return this;
    }

    public GraphicalRobot beginDrag() {
        return beginDrag(1);
    }

    public GraphicalRobot beginDrag(int _mouseX, int _mouseY, int button) {
        moveTo(_mouseX, _mouseY);
        return beginDrag(button);
    }

    public GraphicalRobot beginDrag(Object object, int _mouseX, int _mouseY) {
        Point location = getLocation(object, _mouseX, _mouseY);
        return beginDrag(location.x, location.y, 1);
    }

    public GraphicalRobot beginDrag(Object object) {
        return beginDrag(object, 0, 0);
    }

    public GraphicalRobot dragOn(int deltaX, int deltaY) {
        mouseX += deltaX;
        mouseY += deltaY;
        return drag();
    }

    public GraphicalRobot dragTo(int _mouseX, int _mouseY) {
        mouseX = _mouseX;
        mouseY = _mouseY;
        return drag();
    }

    public GraphicalRobot dragTo(Object object, int _mouseX, int _mouseY) {
        target(object);
        in(_mouseX, _mouseY);
        return drag();
    }

    public GraphicalRobot dragTo(Object object, double _mouseX, double _mouseY) {
        target(object);
        in(_mouseX, _mouseY);
        return drag();
    }

    public GraphicalRobot dragTo(Object object) {
        return dragTo(object, 0, 0);
    }

    public GraphicalRobot drag() {
        int deltaX = mouseX - mouseOldX;
        int deltaY = mouseY - mouseOldY;
        int deltaX1 = deltaX / 2;
        int deltaX2 = deltaX - deltaX1;
        int deltaY1 = deltaY / 2;
        int deltaY2 = deltaY - deltaY1;
        // step 1
        mouseOldX += deltaX1;
        mouseOldY += deltaY1;
        m_sender.dragTo(mouseOldX, mouseOldY);
        // step 2
        mouseOldX += deltaX2;
        mouseOldY += deltaY2;
        m_sender.dragTo(mouseOldX, mouseOldY);
        // done
        return this;
    }

    public void endDrag() {
        m_sender.endDrag();
    }

    /**
     * Performs left button click, for example to finish CREATE operation.
     */
    public GraphicalRobot click(int button) {
        m_sender.click(button);
        return this;
    }

    public GraphicalRobot click() {
        click(1);
        return this;
    }

    public GraphicalRobot click(int x, int y, int button) {
        m_sender.click(x, y, button);
        return this;
    }

    public GraphicalRobot click(Object object, int deltaX, int deltaY) {
        Point location = getLocation(object, deltaX, deltaY);
        click(location.x, location.y, 1);
        return this;
    }

    public GraphicalRobot click(Object object) {
        click(object, 0, 0);
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // doubleClick()
    //
    ////////////////////////////////////////////////////////////////////////////
    public void doubleClick() {
        m_sender.doubleClick(mouseX, mouseY, 1);
    }

    public void doubleClick(Object object) {
        doubleClick(object, 1);
    }

    public void doubleClick(Object object, int button) {
        GraphicalEditPart editPart = getEditPart(object);
        Rectangle bounds = getAbsoluteBounds(editPart);
        Point location = getLocation(editPart, bounds.width / 2, bounds.height / 2);
        m_sender.doubleClick(location.x, location.y, button);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Direct edit
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Performs direct edit and sets given text.
     */
    public void performDirectEdit(Object object, String text) {
        select(object);
        performDirectEdit(text);
    }

    /**
     * Performs direct edit and sets given text.
     */
    public void performDirectEdit(String text) {
        // begin direct edit
        beginDirectEdit();
        // animate
        animateDirectEdit(text);
    }

    /**
     * Animates already activated direct edit.
     */
    public void animateDirectEdit(String text) {
        // prepare Text widget
        Text textWidget = UiContext.findFirstWidget(m_viewer.getControl(), Text.class);
        assertThat(textWidget).describedAs("No Text widget.").isNotNull();
        // use Text widget
        textWidget.setText(text);
        endDirectEdit(textWidget);
    }

    /**
     * Begins direct edit for selected component.
     */
    public void beginDirectEdit() {
        keyDown(0x20, ' ');
    }

    /**
     * Ends direct edit for selected component.
     */
    public void endDirectEdit(Text textWidget) {
        new EventSender(textWidget).keyDown(SWT.CR);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Utils
    //
    ////////////////////////////////////////////////////////////////////////////
    public GraphicalEditPart getEditPart(final Object object) {
        GraphicalEditPart editPart = getEditPartNull(object);
        assertThat(editPart).as(new Description() {
            public String value() {
                return "No EditPart for " + object;
            }
        }).isNotNull();
        return editPart;
    }

    public GraphicalEditPart getEditPartNull(Object object) {
        GraphicalEditPart editPart = object instanceof GraphicalEditPart ? (GraphicalEditPart) object
                : (GraphicalEditPart) m_viewer.getEditPartByModel(object);
        return editPart;
    }

    public GraphicalEditPart[] getEditParts(Object[] objects) {
        GraphicalEditPart[] editParts = new GraphicalEditPart[objects.length];
        for (int i = 0; i < objects.length; i++) {
            editParts[i] = getEditPart(objects[i]);
        }
        return editParts;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Location utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return bounds of {@link GraphicalEditPart}'s figure in absolute coordinates.
     */
    public static Rectangle getAbsoluteBounds(GraphicalEditPart editPart) {
        Figure figure = editPart.getFigure();
        Rectangle bounds = figure.getBounds().getCopy();
        FigureUtils.translateFigureToAbsolute(figure, bounds);
        return bounds;
    }

    public Rectangle getAbsoluteBounds(Object object) {
        return getAbsoluteBounds(getEditPart(object));
    }

    /**
     * @return location of given {@link EditPart} with offset. Negative offset means offset from
     *         right/bottom side.
     */
    public static Point getLocation(GraphicalEditPart editPart, int deltaX, int deltaY) {
        Rectangle bounds = getAbsoluteBounds(editPart);
        Point location = new Point(0, 0);
        if (deltaX >= 0) {
            location.x = bounds.x + deltaX;
        } else {
            location.x = bounds.right() + deltaX;
        }
        if (deltaY >= 0) {
            location.y = bounds.y + deltaY;
        } else {
            location.y = bounds.bottom() + deltaY;
        }
        return location;
    }

    public Point getLocation(Object object, int deltaX, int deltaY) {
        return getLocation(getEditPart(object), deltaX, deltaY);
    }

    public Point getLocation(Object object) {
        return getLocation(object, 0, 0);
    }

    /**
     * @return the size of given {@link EditPart}.
     */
    public static Dimension getSize(GraphicalEditPart editPart) {
        return editPart.getFigure().getSize();
    }

    public Dimension getSize(Object object) {
        return getSize(getEditPart(object));
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Feedback utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Description of {@link Figure}.
     */
    public static final class FigureDescription {
        private final Class<?> m_class;
        private final Rectangle m_absoluteBounds;

        ////////////////////////////////////////////////////////////////////////////
        //
        // Constructor
        //
        ////////////////////////////////////////////////////////////////////////////
        public FigureDescription(Class<?> clazz, Rectangle absoluteBounds) {
            m_class = clazz;
            m_absoluteBounds = absoluteBounds;
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Object
        //
        ////////////////////////////////////////////////////////////////////////////
        @Override
        public String toString() {
            return "(" + m_class.getName() + ", " + m_absoluteBounds + ")";
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Access
        //
        ////////////////////////////////////////////////////////////////////////////
        /**
         * @return <code>true</code> if given {@link Figure} matches this {@link FigureDescription}.
         */
        public boolean match(Figure figure) {
            boolean match = true;
            match &= m_class.isAssignableFrom(figure.getClass());
            if (m_absoluteBounds != null) {
                match &= m_absoluteBounds.equals(figure.getBounds());
            }
            return match;
        }
    }

    /**
     * @return the list of {@link Figure}'s on feedback {@link Layer}.
     */
    public List<Figure> getFeedbackFigures() {
        return m_viewer.getLayer(IEditPartViewer.FEEDBACK_LAYER).getChildren();
    }

    /**
     * @return the current {@link Command} from currently loaded {@link Tool}.
     */
    public Command getCommand() throws Exception {
        Tool tool = m_viewer.getEditDomain().getActiveTool();
        // when drag is in progress, ask command from "drag tracker"
        if (tool instanceof SelectionTool) {
            Tool dragTracker = (Tool) ReflectionUtils.getFieldObject(tool, "m_dragTracker");
            if (dragTracker != null) {
                tool = dragTracker;
            }
        }
        // OK, get Command from active tool
        return (Command) ReflectionUtils.getFieldObject(tool, "m_command");
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Assertions
    //
    ////////////////////////////////////////////////////////////////////////////
    public void assertNullEditPart(Object object) {
        assertThat(getEditPartNull(object)).isNull();
    }

    public void assertNotNullEditPart(Object object) {
        assertThat(getEditPartNull(object)).isNotNull();
    }

    /**
     * Asserts that selection is empty, i.e. no {@link EditPart} selected.
     */
    public GraphicalRobot assertSelectedEmpty() {
        List<EditPart> selectedEditParts = m_viewer.getSelectedEditParts();
        assertThat(selectedEditParts).isEmpty();
        return this;
    }

    public void assertPrimarySelected(Object object) {
        EditPart editPart = getEditPart(object);
        assertThat(editPart.getSelected()).isEqualTo(EditPart.SELECTED_PRIMARY);
    }

    /**
     * Assert that selection contains exactly given {@link EditPart}s.
     */
    public void assertSelection(Object... objects) {
        List<EditPart> selectedEditParts = m_viewer.getSelectedEditParts();
        GraphicalEditPart[] editParts = getEditParts(objects);
        assertThat(selectedEditParts).containsExactly((Object[]) editParts);
    }

    public void assertChildrenCount(Object object, int count) {
        EditPart editPart = getEditPart(object);
        assertThat(editPart.getChildren()).hasSize(count);
    }

    /**
     * Asserts that {@link Layer} has {@link Figure}'s that satisfy to given {@link Predicate} .
     */
    @SuppressWarnings("unchecked")
    public void assertFigures(String layerName, Predicate<Figure> predicate) {
        assertFigures(layerName, new Predicate[] { predicate });
    }

    /**
     * Asserts that {@link Layer} has {@link Figure}'s that satisfy to given {@link Predicate} 's.
     */
    public void assertFigures(String layerName, Predicate<Figure>... predicates) {
        // prepare feedback's
        List<Figure> feedbacks;
        {
            Layer feedbackLayer = m_viewer.getLayer(layerName);
            feedbacks = feedbackLayer.getChildren();
            assertThat(feedbacks.size()).describedAs("Wrong count of feedbacks.").isEqualTo(predicates.length);
        }
        // check all feedback's
        for (int i = 0; i < predicates.length; i++) {
            Predicate<Figure> predicate = predicates[i];
            Figure feedback = feedbacks.get(i);
            assertThat(predicate.apply(feedback)).describedAs("Predicate [" + i + "] failed.").isTrue();
        }
    }

    /**
     * Asserts that there are no any feedback on {@link IEditPartViewer#FEEDBACK_LAYER}.
     */
    public void assertNoFeedbacks() {
        assertFigures(IEditPartViewer.FEEDBACK_LAYER);
    }

    /**
     * Asserts that given host has "empty flow container" line feedback on
     * {@link IEditPartViewer#FEEDBACK_LAYER}.
     */
    public void assertEmptyFlowContainerFeedback(Object host, boolean horizontal) {
        Predicate<Figure> predicate = getEmptyFlowContainerPredicate(host, horizontal);
        assertFeedbacks(predicate);
    }

    /**
     * Asserts that {@link IEditPartViewer#FEEDBACK_LAYER} has feedback {@link Figure}'s that satisfy
     * to given {@link Predicate}'s.
     */
    @SuppressWarnings("unchecked")
    public void assertFeedbacks(Predicate<Figure> predicate_1) {
        assertFeedbacks0(predicate_1);
    }

    /**
     * Asserts that {@link IEditPartViewer#FEEDBACK_LAYER} has feedback {@link Figure}'s that satisfy
     * to given {@link Predicate}'s.
     */
    private void assertFeedbacks0(Predicate<Figure>... predicates) {
        assertFigures(IEditPartViewer.FEEDBACK_LAYER, predicates);
    }

    /**
     * Asserts that feedback layer contains exactly same {@link Figure}'s as described.
     */
    public void assertFeedbackFigures(FigureDescription... descriptions) {
        HashSet<Figure> feedbackFigures = new HashSet<Figure>(getFeedbackFigures());
        //
        for (int i = 0; i < descriptions.length; i++) {
            FigureDescription description = descriptions[i];
            // try to find figure for current description
            boolean figureFound = false;
            for (Iterator<Figure> I = feedbackFigures.iterator(); I.hasNext();) {
                Figure figure = I.next();
                if (description.match(figure)) {
                    I.remove();
                    figureFound = true;
                    break;
                }
            }
            // figure should be found
            assertThat(figureFound).describedAs("No figure found for " + description).isTrue();
        }
        // all figure should be matched
        if (!feedbackFigures.isEmpty()) {
            String message = "Following figures are not matched:";
            for (Figure figure : feedbackFigures) {
                message += "\n\t" + figure.getClass().getName() + " " + figure.getBounds() + " "
                        + figure.toString();
            }
            assertThat(true).describedAs(message).isFalse();
        }
    }

    /**
     * Asserts that there are no {@link Figure}'s on feedback {@link Layer}.
     */
    public void assertNoFeedbackFigures() {
        assertThat(getFeedbackFigures().isEmpty()).describedAs("Feedback layer should be empty.").isTrue();
    }

    /**
     * Asserts that there are exactly given count of {@link Figure}'s on feedback {@link Layer}.
     */
    public void assertFeedbackFigures(int count) {
        assertThat(getFeedbackFigures()).hasSize(count);
    }

    /**
     * Asserts that currently loaded {@link Tool} has <code>null</code> as command.
     */
    public GraphicalRobot assertCommandNull() throws Exception {
        final Command command = getCommand();
        assertThat(command).describedAs(new Description() {
            public String value() {
                return "Unexpected command " + command;
            }
        }).isNull();
        return this;
    }

    /**
     * Asserts that currently loaded {@link Tool} has not <code>null</code> as command.
     */
    public GraphicalRobot assertCommandNotNull() throws Exception {
        Command command = getCommand();
        assertThat(command).describedAs("No command.").isNotNull();
        return this;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Figure predicates
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link Predicate} that checks if feedback is line used for empty flow container.
     */
    public final Predicate<Figure> getEmptyFlowContainerPredicate(Object hostModel, boolean horizontal) {
        GraphicalEditPart host = getEditPart(hostModel);
        // prepare "host" Figure bounds in absolute
        Rectangle bounds;
        {
            bounds = host.getFigure().getBounds().getCopy();
            FigureUtils.translateFigureToAbsolute(host.getFigure(), bounds);
        }
        // prepare expected points
        final Point p1;
        final Point p2;
        if (horizontal) {
            p1 = new Point(bounds.x, bounds.y);
            p2 = new Point(bounds.x, bounds.bottom());
        } else {
            p1 = new Point(bounds.x, bounds.y);
            p2 = new Point(bounds.right(), bounds.y);
        }
        // if host is big enough, tweak points for better look
        if (horizontal) {
            if (bounds.width > 20) {
                p1.x += 5;
                p2.x += 5;
            }
            if (bounds.height > 20) {
                p1.y += 5;
                p2.y -= 5;
            }
        } else {
            if (bounds.width > 20) {
                p1.x += 5;
                p2.x -= 5;
            }
            if (bounds.height > 20) {
                p1.y += 5;
                p2.y += 5;
            }
        }
        // return predicate
        return new Predicate<Figure>() {
            public boolean apply(Figure feedback) {
                if (!(feedback instanceof Polyline)) {
                    return false;
                }
                // prepare points of line
                Point p1_;
                Point p2_;
                {
                    Polyline polyline = (Polyline) feedback;
                    PointList points = polyline.getPoints();
                    if (points.size() != 2) {
                        return false;
                    }
                    p1_ = points.getPoint(0);
                    p2_ = points.getPoint(1);
                }
                // compare
                return p1_.equals(p1) && p2_.equals(p2);
            }
        };
    }

    /**
     * @return the {@link Predicate} that checks if feedback is relative given
     *         {@link GraphicalEditPart}.
     * 
     * @param part
     *          the {@link GraphicalEditPart} relative to which feedback is expected.
     * @param location
     *          the location of feedback, one of {@link IPositionConstants#TOP},
     *          {@link IPositionConstants#BOTTOM} , {@link IPositionConstants#LEFT},
     *          {@link IPositionConstants#RIGHT}.
     */
    public static final Predicate<Figure> getLinePredicate(GraphicalEditPart part, final int location) {
        // prepare "part" Figure bounds in absolute
        final Rectangle partBounds;
        {
            partBounds = part.getFigure().getBounds().getCopy();
            FigureUtils.translateFigureToAbsolute(part.getFigure(), partBounds);
        }
        // return predicate
        return new Predicate<Figure>() {
            public boolean apply(Figure feedback) {
                if (!(feedback instanceof Polyline)) {
                    return false;
                }
                // prepare points of line
                Point p1;
                Point p2;
                {
                    Polyline polyline = (Polyline) feedback;
                    PointList points = polyline.getPoints();
                    if (points.size() != 2) {
                        return false;
                    }
                    p1 = points.getPoint(0);
                    p2 = points.getPoint(1);
                }
                // checks that line has expected location
                int delta = 5;
                boolean result = true;
                if (location == IPositionConstants.TOP) {
                    result &= p1.y == p2.y;
                    result &= Math.abs(p1.y - partBounds.y) < delta;
                    result &= Math.abs(p1.x - partBounds.x) < delta;
                    result &= Math.abs(p2.x - partBounds.right()) < delta;
                } else if (location == IPositionConstants.BOTTOM) {
                    result &= p1.y == p2.y;
                    result &= Math.abs(p1.y - partBounds.bottom()) < delta;
                    result &= Math.abs(p1.x - partBounds.x) < delta;
                    result &= Math.abs(p2.x - partBounds.right()) < delta;
                } else if (location == IPositionConstants.LEFT) {
                    result &= p1.x == p2.x;
                    result &= Math.abs(p1.x - partBounds.x) < delta;
                    result &= Math.abs(p1.y - partBounds.y) < delta;
                    result &= Math.abs(p2.y - partBounds.bottom()) < delta;
                } else if (location == IPositionConstants.RIGHT) {
                    result &= p1.x == p2.x;
                    result &= Math.abs(p1.x - partBounds.right()) < delta;
                    result &= Math.abs(p1.y - partBounds.y) < delta;
                    result &= Math.abs(p2.y - partBounds.bottom()) < delta;
                } else {
                    Assert.fail("Unsupported location: " + location);
                }
                // OK, final result
                return result;
            }
        };
    }

    public final Predicate<Figure> getLinePredicate(Object object, final int location) {
        return getLinePredicate(getEditPart(object), location);
    }

    /**
     * @return the {@link Predicate} that checks if feedback is "border target" around given
     *         {@link GraphicalEditPart}.
     */
    public static final Predicate<Figure> getTargetPredicate(GraphicalEditPart part) {
        // prepare "part" Figure bounds in absolute
        final Rectangle partBounds;
        {
            partBounds = part.getFigure().getBounds().getCopy();
            FigureUtils.translateFigureToAbsolute(part.getFigure(), partBounds);
            partBounds.expand(3, 3);
        }
        // return predicate
        return new Predicate<Figure>() {
            public boolean apply(Figure feedback) {
                return partBounds.equals(feedback.getBounds());
            }
        };
    }

    public final Predicate<Figure> getTargetPredicate(Object object) {
        return getTargetPredicate(getEditPart(object));
    }
}