public class Commands
{

    private static final Logger LOGGER = LoggerFactory.getLogger(Commands.class);

    private static final EReference NODES = GraphPackage.Literals.GMODEL__NODES;
    private static final EReference CONNECTIONS = GraphPackage.Literals.GMODEL__CONNECTIONS;

    private static final EAttribute NODE_X = GraphPackage.Literals.GNODE__X;
    private static final EAttribute NODE_Y = GraphPackage.Literals.GNODE__Y;
    private static final EAttribute NODE_WIDTH = GraphPackage.Literals.GNODE__WIDTH;
    private static final EAttribute NODE_HEIGHT = GraphPackage.Literals.GNODE__HEIGHT;
    private static final EReference NODE_CONNECTORS = GraphPackage.Literals.GNODE__CONNECTORS;

    private static final EReference CONNECTOR_CONNECTIONS = GraphPackage.Literals.GCONNECTOR__CONNECTIONS;

    private static final EAttribute JOINT_X = GraphPackage.Literals.GJOINT__X;
    private static final EAttribute JOINT_Y = GraphPackage.Literals.GJOINT__Y;

    /**
     * Static class, not to be instantiated.
     */
    private Commands()
    {
    }

    /**
     * Adds a node to the model.
     *
     * <p>
     * The node's x, y, width, and height values should be set before calling this method.
     * </p>
     *
     * @param model the {@link GModel} to which the node should be added
     * @param node the {@link GNode} to add to the model
     */
    public static void addNode(final GModel model, final GNode node)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null)
        {
            final Command command = AddCommand.create(editingDomain, model, NODES, node);

            if (command.canExecute())
            {
                editingDomain.getCommandStack().execute(command);
            }
        }
    }

    /**
     * Removes a node from the model.
     *
     * <p>
     * Also removes any connections that were attached to the node.
     * </p>
     *
     * @param model the {@link GModel} from which the node should be removed
     * @param node the {@link GNode} to remove from the model
     */
    public static void removeNode(final GModel model, final GNode node)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null)
        {
            final CompoundCommand command = new CompoundCommand();
            command.append(RemoveCommand.create(editingDomain, model, NODES, node));

            final List<GConnection> connectionsToDelete = new ArrayList<>();

            for (final GConnector connector : node.getConnectors())
            {
                for (final GConnection connection : connector.getConnections())
                {
                    if (connection != null && !connectionsToDelete.contains(connection))
                    {
                        connectionsToDelete.add(connection);
                    }
                }
            }

            for (final GConnection connection : connectionsToDelete)
            {
                command.append(RemoveCommand.create(editingDomain, model, CONNECTIONS, connection));

                final GConnector source = connection.getSource();
                final GConnector target = connection.getTarget();

                if (!node.equals(source.getParent()))
                {
                    command.append(RemoveCommand.create(editingDomain, source, CONNECTOR_CONNECTIONS, connection));
                }

                if (!node.equals(target.getParent()))
                {
                    command.append(RemoveCommand.create(editingDomain, target, CONNECTOR_CONNECTIONS, connection));
                }
            }

            if (command.canExecute())
            {
                editingDomain.getCommandStack().execute(command);
            }
        }
    }

    /**
     * Clears everything in the given model.
     *
     * @param model the {@link GModel} to be cleared
     */
    public static void clear(final GModel model)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null)
        {
            final CompoundCommand command = new CompoundCommand();

            command.append(RemoveCommand.create(editingDomain, model, CONNECTIONS, model.getConnections()));
            command.append(RemoveCommand.create(editingDomain, model, NODES, model.getNodes()));

            if (command.canExecute())
            {
                editingDomain.getCommandStack().execute(command);
            }
        }
    }

    /**
     * Removes all connectors from the given nodes, and all connections attached to them.
     *
     * @param model the {@link GModel} being edited
     * @param nodes a list of {@link GNode} instances whose connectors should be removed
     */
    public static void clearConnectors(final GModel model, final List<GNode> nodes)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null)
        {
            final CompoundCommand command = new CompoundCommand();

            final Set<GConnection> connectionsToRemove = new HashSet<>();
            final Set<GConnector> connectorsToRemove = new HashSet<>();

            for (final GNode node : nodes)
            {
                command.append(RemoveCommand.create(editingDomain, node, NODE_CONNECTORS, node.getConnectors()));
                connectorsToRemove.addAll(node.getConnectors());

                for (final GConnector connector : node.getConnectors())
                {
                    connectionsToRemove.addAll(connector.getConnections());
                }
            }

            for (final GConnection connection : connectionsToRemove)
            {
                final GConnector source = connection.getSource();
                final GConnector target = connection.getTarget();

                if (!connectorsToRemove.contains(source))
                {
                    command.append(RemoveCommand.create(editingDomain, source, CONNECTOR_CONNECTIONS, connection));
                }

                if (!connectorsToRemove.contains(target))
                {
                    command.append(RemoveCommand.create(editingDomain, target, CONNECTOR_CONNECTIONS, connection));
                }
            }

            command.append(RemoveCommand.create(editingDomain, model, CONNECTIONS, connectionsToRemove));

            if (command.canExecute())
            {
                editingDomain.getCommandStack().execute(command);
            }
        }
    }

    /**
     * Updates the model's layout values to match those in the skin instances.
     *
     * <p>
     * This method adds set operations to the given compound command but does <b>not</b> execute it.
     * </p>
     *
     * @param command a {@link CompoundCommand} to which the set commands will be added
     * @param model the {@link GModel} whose layout values should be updated
     * @param skinLookup the {@link SkinLookup} in use for this graph editor instance
     */
    public static void updateLayoutValues(final CompoundCommand command, final GModel model,
            final SkinLookup skinLookup)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null)
        {
            for (final GNode node : model.getNodes())
            {
                final GNodeSkin nodeSkin = skinLookup.lookupNode(node);
                if (nodeSkin != null && checkNodeChanged(node, nodeSkin))
                {
                    final Region nodeRegion = nodeSkin.getRoot();
                    command.append(SetCommand.create(editingDomain, node, NODE_X, nodeRegion.getLayoutX()));
                    command.append(SetCommand.create(editingDomain, node, NODE_Y, nodeRegion.getLayoutY()));
                    command.append(SetCommand.create(editingDomain, node, NODE_WIDTH, nodeRegion.getWidth()));
                    command.append(SetCommand.create(editingDomain, node, NODE_HEIGHT, nodeRegion.getHeight()));
                }
            }

            for (final GConnection connection : model.getConnections())
            {
                updateConnector(connection.getSource(), command, editingDomain, skinLookup);
                updateConnector(connection.getTarget(), command, editingDomain, skinLookup);

                for (final GJoint joint : connection.getJoints())
                {
                    final GJointSkin jointSkin = skinLookup.lookupJoint(joint);
                    if (jointSkin != null && checkJointChanged(joint, jointSkin))
                    {
                        final Region jointRegion = jointSkin.getRoot();
                        final double x = jointRegion.getLayoutX() + jointSkin.getWidth() / 2;
                        final double y = jointRegion.getLayoutY() + jointSkin.getHeight() / 2;

                        command.append(SetCommand.create(editingDomain, joint, JOINT_X, x));
                        command.append(SetCommand.create(editingDomain, joint, JOINT_Y, y));
                    }
                }
            }
        }
    }

    private static void updateConnector(final GConnector connector, final CompoundCommand command,
            final EditingDomain editingDomain, final SkinLookup skinLookup)
    {
        final GNode node = connector.getParent();
        final GConnectorSkin connectorSkin = skinLookup.lookupConnector(connector);
        final GNodeSkin nodeSkin = skinLookup.lookupNode(node);
        if (nodeSkin != null && connectorSkin != null)
        {
            final Point2D connectorPosition = nodeSkin.getConnectorPosition(connectorSkin);
            if (checkConnectorChanged(connector, connectorPosition))
            {
                command.append(SetCommand.create(editingDomain, connector, GraphPackage.Literals.GCONNECTOR__X,
                        connectorPosition.getX()));
                command.append(SetCommand.create(editingDomain, connector, GraphPackage.Literals.GCONNECTOR__Y,
                        connectorPosition.getY()));
            }
        }
    }

    /**
     * Checks if a connector's JavaFX region has different layout values than those
     * currently stored in the model.
     *
     * @param connector
     *            the model instance for the connector
     *
     * @return {@code true} if any layout value has changed, {@code false if not}
     */
    private static boolean checkConnectorChanged(final GConnector connector, final Point2D connectorPosition)
    {
        if (connectorPosition.getX() != connector.getX())
        {
            return true;
        }
        else if (connectorPosition.getY() != connector.getY())
        {
            return true;
        }
        return false;
    }

    /**
     * Checks if a node's JavaFX region has different layout values than those
     * currently stored in the model.
     *
     * @param node
     *            the model instance for the node
     *
     * @return {@code true} if any layout value has changed,
     *         {@code false if not}
     */
    private static boolean checkNodeChanged(final GNode node, final GNodeSkin nodeSkin)
    {
        final Region nodeRegion = nodeSkin.getRoot();

        if (nodeRegion.getLayoutX() != node.getX())
        {
            return true;
        }
        else if (nodeRegion.getLayoutY() != node.getY())
        {
            return true;
        }
        else if (nodeRegion.getWidth() != node.getWidth())
        {
            return true;
        }
        else if (nodeRegion.getHeight() != node.getHeight())
        {
            return true;
        }
        return false;
    }

    /**
     * Checks if a joint's JavaFX region has different layout values than those
     * currently stored in the model.
     *
     * @param joint
     *            the model instance for the joint
     *
     * @return {@code true} if any layout value has changed,
     *         {@code false if not}
     */
    private static boolean checkJointChanged(final GJoint joint, final GJointSkin jointSkin)
    {
        final Region jointRegion = jointSkin.getRoot();

        final double jointRegionX = jointRegion.getLayoutX() + jointSkin.getWidth() / 2;
        final double jointRegionY = jointRegion.getLayoutY() + jointSkin.getHeight() / 2;

        if (jointRegionX != joint.getX())
        {
            return true;
        }
        else if (jointRegionY != joint.getY())
        {
            return true;
        }
        return false;
    }

    /**
     * Attempts to undo the given model to its previous state.
     *
     * @param model the {@link GModel} to undo
     */
    public static void undo(final GModel model)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null && editingDomain.getCommandStack().canUndo())
        {
            editingDomain.getCommandStack().undo();
        }
    }

    /**
     * Attempts to redo the given model to its next state.
     *
     * @param model the {@link GModel} to redo
     */
    public static void redo(final GModel model)
    {
        final EditingDomain editingDomain = getEditingDomain(model);

        if (editingDomain != null && editingDomain.getCommandStack().canRedo())
        {
            editingDomain.getCommandStack().redo();
        }
    }

    /**
     * Gets the editing domain associated to the model.
     *
     * <p>
     * Logs an error if none is found.
     * </p>
     *
     * @param model a {@link GModel} instance
     * @return the {@link EditingDomain} associated to this model instance
     */
    private static EditingDomain getEditingDomain(final GModel model)
    {
        final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(model);

        if (editingDomain == null)
        {
            LOGGER.error("No editing domain found for this model. Maybe it hasn't been set inside a graph editor?"); //$NON-NLS-1$
        }

        return editingDomain;
    }
} public interface GConnectorValidator {

    /**
     * Pre-validate check made during drag-over events.
     *
     * <p>
     * If the pre-validate check fails, the dragged connector will not interact with the dragged-over connector at all.
     * </p>
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return {@code true} if a validate check should be made, {@code false} if not
     */
    boolean prevalidate(GConnector source, GConnector target);

    /**
     * Validate check made during drag-over events. Only made if the pre-validate check passes.
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return {@code true} if connection is allowed, {@code false} if it is forbidden
     */
    boolean validate(GConnector source, GConnector target);

    /**
     * Creates the 'type' string to be used in a new connection.
     *
     * <p>
     * If both prevalidate and validate checks pass, a new connection will be created of the type returned by this
     * method.
     * </p>
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return a {@link String} specifying the type for the new connection
     */
    String createConnectionType(GConnector source, GConnector target);

    /**
     * Creates the 'type' string to be used in the joints inside a new connection.
     *
     * @param source the {@link GConnector} that was dragged
     * @param target the {@link GConnector} that was dragged-over
     *
     * @return a {@link String} specifying the type for the new connection
     */
    String createJointType(GConnector source, GConnector target);
}
 public abstract class GJointSkin extends GSkin<GJoint> {

    private final DraggableBox root = new DraggableBox(EditorElement.JOINT)
    {

        @Override
        public final void positionMoved()
        {
            super.positionMoved();
            GJointSkin.this.impl_positionMoved();
        }
    };

    /**
     * Creates a new {@link GJointSkin}.
     *
     * @param joint the {@link GJoint} represented by the skin
     */
    public GJointSkin(final GJoint joint) {
        super(joint);
    }

    /**
     * Gets the root JavaFX node of the skin.
     *
     * @return a {@link DraggableBox} containing the skin's root JavaFX node
     */
    @Override
    public DraggableBox getRoot() {
        return root;
    }

    /**
     * Initializes the joint skin.
     *
     * <p>
     * The skin's layout values are loaded from the {@link GJoint} at this point.
     * </p>
     */
    public void initialize() {
        getRoot().setLayoutX(getItem().getX() - getWidth() / 2);
        getRoot().setLayoutY(getItem().getY() - getHeight() / 2);
    }

    /**
     * Gets the width of the joint.
     *
     * <p>
     * For robust behaviour this should return the correct width at all times. Note that getRoot().getWidth() does not
     * meet this condition as it will return 0 until the joint's node is added to the scene graph and layed out.
     * </p>
     *
     * @return the width of the joint
     */
    public abstract double getWidth();

    /**
     * Gets the height of the joint.
     *
     * <p>
     * For robust behaviour this should return the correct height at all times. Note that getRoot().getHeight() does not
     * meet this condition as it will return 0 until the joint's node is added to the scene graph and layed out.
     * </p>
     *
     * @return the height of the joint
     */
    public abstract double getHeight();
} public abstract class GNodeSkin extends GSkin<GNode> {

    private final DraggableBox root;

    /**
     * Creates a new {@link GNodeSkin}.
     *
     * @param node the {@link GNode} represented by the skin
     */
    public GNodeSkin(final GNode node) {
        super(node);
        root = createContainer();
    }

    /**
     * Gets the root JavaFX node of the skin.
     *
     * @return a {@link ResizableBox} containing the skin's root JavaFX node
     */
    @Override
    public DraggableBox getRoot()
    {
        return root;
    }

    /**
     * Initializes the node skin.
     *
     * <p>
     * The skin's layout values, e.g. its x and y position, are loaded from the {@link GNode} at this point.
     * </p>
     */
    public void initialize() {

        getRoot().setLayoutX(getItem().getX());
        getRoot().setLayoutY(getItem().getY());

        getRoot().resize(getItem().getWidth(), getItem().getHeight());
    }

    /**
     * Sets the node's connector skins.
     *
     * <p>
     * This will be called as the node is created, or if a connector is added or removed. The connector skin's regions
     * should be added to the scene graph.
     * </p>
     *
     * @param connectorSkins a list of {@link GConnectorSkin} objects for each of the node's connectors
     */
    public abstract void setConnectorSkins(List<GConnectorSkin> connectorSkins);

    /**
     * Lays out the node's connectors.
     */
    public abstract void layoutConnectors();

    /**
     * Gets the position of the <b>center</b> of a connector relative to the node region.
     *
     * <p>
     * This will be the point where a connection will attach to.
     * </p>
     *
     * @param connectorSkin a {@link GConnectorSkin} instance
     *
     * @return the x and y coordinates of the connector
     */
    public abstract Point2D getConnectorPosition(GConnectorSkin connectorSkin);

    /**
     * Creates and returns the {@link DraggableBox} that serves as the root for
     * this node skin.<br>
     * By default a {@link ResizableBox} will be created and return as most
     * nodes will be both draggable and resizable.
     *
     * @return {@link DraggableBox}
     */
    protected DraggableBox createContainer()
    {
        return new ResizableBox(EditorElement.NODE)
        {

            @Override
            protected void layoutChildren()
            {
                super.layoutChildren();
                layoutConnectors();
            }

            @Override
            public void positionMoved()
            {
                super.positionMoved();
                GNodeSkin.this.impl_positionMoved();
            }
        };
    }
} public interface GraphEditor extends GraphEditorSkins {

    /**
     * Sets a custom connector validator.
     *
     * <p>
     * This will be used to decide which connections are allowed / forbidden during drag and drop events in the editor.
     * </p>
     *
     * @param validator a custom validator implements {@link GConnectorValidator}, or null to use the default validator
     */
    void setConnectorValidator(final GConnectorValidator validator);

    /**
     * Sets the graph model to be edited.
     *
     * @param model the {@link GModel} to be edited
     */
    void setModel(final GModel model);

    /**
     * Gets the graph model that is currently being edited.
     *
     * @return the {@link GModel} being edited, or {@code null} if no model was ever set
     */
    GModel getModel();

    /**
     * Reloads the graph model currently being edited.
     *
     * <p>
     * <b>Note: </b><br>
     * If the model is updated via EMF commands, as is recommended, it should rarely be necessary to call this method.
     * The model will be reloaded automatically via a command-stack listener.
     * </p>
     */
    void reload();

    /**
     * The property containing the graph model being edited.
     *
     * @return a property containing the {@link GModel} being edited
     */
    ObjectProperty<GModel> modelProperty();

    /**
     * Gets the view where the graph is displayed and edited.
     *
     * <p>
     * The view is a JavaFX {@link Region}. It should be added to the scene
     * graph in the usual way.
     * </p>
     *
     * @return the {@link Region} where the graph is displayed and edited
     */
    Region getView();

    /**
     * Gets the properties of the editor.
     *
     * <p>
     * This provides access to global properties such as:
     *
     * <ul>
     * <li>Show/hide alignment grid.</li>
     * <li>Toggle snap-to-grid on/off.</li>
     * <li>Toggle editor bounds on/off.</li>
     * </ul>
     *
     * </p>
     *
     * @return an {@link GraphEditorProperties} instance containing the properties of the editor
     */
    GraphEditorProperties getProperties();

    /**
     * Gets the skin lookup.
     *
     * <p>
     * The skin lookup is used to get any skin instance associated to a model element instance.
     * </p>
     *
     * @return a {@link SkinLookup} used to lookup skins
     */
    SkinLookup getSkinLookup();

    /**
     * Gets the selection manager.
     *
     * <p>
     * The selection manager keeps track of the selected nodes, connections,
     * etc.
     * </p>
     *
     * @return the {@link SelectionManager}
     */
    SelectionManager getSelectionManager();

    /**
     * Sets a method to be called when a connection is created in the editor.
     *
     * <p>
     * This can be used to append additional commands to the one that created the connection.
     * </p>
     *
     * @param consumer a consumer to append additional commands
     */
    void setOnConnectionCreated(Function<GConnection, Command> consumer);

    /**
     * Sets a method to be called when a connection is removed in the editor.
     *
     * <p>
     * This can be used to create additional commands to the one that removed
     * the connection.
     * </p>
     *
     * @param pOnConnectionRemoved
     *            a {@link BiFunction} creating the additional command
     */
    void setOnConnectionRemoved(BiFunction<RemoveContext, GConnection, Command> pOnConnectionRemoved);

    /**
     * Sets a method to be called when a node is removed in the editor.
     *
     * <p>
     * This can be used to create additional commands to the one that removed
     * the node.
     * </p>
     *
     * @param pOnNodeRemoved
     *            a {@link BiFunction} creating the additional command
     */
    void setOnNodeRemoved(BiFunction<RemoveContext, GNode, Command> pOnNodeRemoved);

    /**
     * Deletes all elements that are currently selected.
     *
     * @param pItems
     *            the items to remove from the graph
     */
    void delete(Collection<EObject> pItems);
} public interface SelectionManager {

    /**
     * Gets the list of currently selected nodes.
     *
     * <p>
     * This list is read-only. Nodes should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected nodes
     */
    List<GNode> getSelectedNodes();
    
    /**
     * Gets the list of currently selected connections.
     *
     * <p>
     * This list is read-only. Connections should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected connections
     */
    List<GConnection> getSelectedConnections();

    /**
     * Gets the list of currently selected joints.
     *
     * <p>
     * This list is read-only. Joints should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the unmodifiable list of currently selected joints
     */
    List<GJoint> getSelectedJoints();
    
    /**
     * Convenience method to inform if the given object is currently selected. Is
     * functionally equivalent to calling
     * <code>getSelectedItems().contains(object)</code>.
     * 
     * @param object
     * @return {@code true} if the given index is selected, {@code false} otherwise.
     */
    boolean isSelected(EObject object);
    
    /**
     * Gets the {@link ObservableSet} of currently-selected items.
     *
     * <p>
     * This set is read-only. Items should be selected via {@link #select(EObject)}.
     * </p>
     *
     * @return the set of selected items
     */
    ObservableSet<EObject> getSelectedItems();
    
    /**
     * This method will attempt to select the given object.
     *
     * @param object The object to attempt to select in the underlying data model.
     */
    void select(EObject object);
    
    /**
     * Selects all selectable elements (nodes, joints, and connections) in the graph editor.
     */
    void selectAll();
    
    /**
     * This method will clear the selection of the given object.
     * If the given object is not selected, nothing will happen.
     *
     * @param object The selected item to deselect.
     */
    void clearSelection(EObject object);

    /**
     * Clears the selection, i.e. de-selects all elements.
     */
    void clearSelection();
} public interface SkinLookup {

    /**
     * Gets the skin for the given node.
     *
     * @param node a {@link GNode} instance
     *
     * @return the associated {@link GNodeSkin} instance
     */
    GNodeSkin lookupNode(final GNode node);

    /**
     * Gets the skin for the given connector.
     *
     * @param connector a {@link GConnector} instance
     *
     * @return the associated {@link GConnectorSkin} instance
     */
    GConnectorSkin lookupConnector(final GConnector connector);

    /**
     * Gets the skin for the given connection.
     *
     * @param connection a {@link GConnection} instance
     *
     * @return the associated {@link GConnectionSkin} instance
     */
    GConnectionSkin lookupConnection(final GConnection connection);

    /**
     * Gets the skin for the given joint.
     *
     * @param joint a {@link GJoint} instance
     *
     * @return the associated {@link GJointSkin} instance
     */
    GJointSkin lookupJoint(final GJoint joint);

    /**
     * Gets the tail skin for the given connector.
     *
     * @param connector a {@link GConnector} instance
     *
     * @return the associated {@link GTailSkin} instance
     */
    GTailSkin lookupTail(final GConnector connector);
} public class ConnectionEventManager {

    private Function<GConnection, Command> connectionCreatedHandler;
    private BiFunction<RemoveContext, GConnection, Command> connectionRemovedHandler;

    /**
     * Sets the handler to be called when connections are created.
     *
     * @param connectionCreatedHandler the handler to be called when connections are created
     */
    public void setOnConnectionCreated(final Function<GConnection, Command> connectionCreatedHandler)
    {
        this.connectionCreatedHandler = connectionCreatedHandler;
    }

    /**
     * Sets the handler to be called when connections are removed.
     *
     * @param connectionRemovedHandler the handler to be called when connections are removed
     */
    public void setOnConnectionRemoved(final BiFunction<RemoveContext, GConnection, Command> connectionRemovedHandler)
    {
        this.connectionRemovedHandler = connectionRemovedHandler;
    }

    /**
     * Calls the connection-created handler (if it exists) after a connection was created.
     *
     * @param connection the connection that was created
     * @return the compound command that created it
     */
    public Command notifyConnectionAdded(final GConnection connection)
    {
        return connectionCreatedHandler == null ? null : connectionCreatedHandler.apply(connection);
    }

    /**
     * Calls the connection-removed handler (if it exists) after a connection was removed.
     *
     * @param connection the connection that was removed
     * @return the compound command that removed it
     */
    public Command notifyConnectionRemoved(final GConnection connection)
    {
        if (connectionRemovedHandler == null)
        {
            return null;
        }
        final RemoveContext context = new RemoveContext();
        context.canRemove(connection);
        return connectionRemovedHandler.apply(context, connection);
    }
}
 public class ConnectorDragManager {

    private final TailManager tailManager;

    private final GraphEditorView view;
    private final SkinLookup skinLookup;
    private final ConnectionEventManager connectionEventManager;

    private GModel model;

    private final EventHandler<MouseEvent> mouseExitedHandler = this::handleMouseExited;
    /**
     * Consume the Event so the parent container (ResizableBox/DraggableBox) does
     * not move on connection detach
     */
    private final EventHandler<MouseEvent> mousePressedHandler = Event::consume;

    private final Map<Node, EventHandler<MouseEvent>> mouseEnteredHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseEvent>> mouseReleasedHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseEvent>> dragDetectedHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseEvent>> mouseDraggedHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseDragEvent>> mouseDragEnteredHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseDragEvent>> mouseDragExitedHandlers = new HashMap<>();
    private final Map<Node, EventHandler<MouseDragEvent>> mouseDragReleasedHandlers = new HashMap<>();

    private GConnectorValidator validator = new DefaultConnectorValidator();

    private GConnector hoveredConnector;
    private GConnector sourceConnector;
    private GConnector targetConnector;
    private GConnector removalConnector;

    private boolean repositionAllowed;

    /**
     * Creates a new {@link ConnectorDragManager}. Only one instance should
     * exist per {@link DefaultGraphEditor} instance.
     *
     * @param skinLookup
     *            the {@link SkinLookup} used to look up connector and tail
     *            skins
     * @param connectionEventManager
     *            the {@link ConnectionEventManager} used to notify users of
     *            connection events
     * @param view
     *            the {@link GraphEditorView} to which tail skins will be added
     *            and removed during drag events
     */
    public ConnectorDragManager(final SkinLookup skinLookup, final ConnectionEventManager connectionEventManager,
            final GraphEditorView view)
    {
        this.view = view;
        this.skinLookup = skinLookup;
        this.connectionEventManager = connectionEventManager;
        this.tailManager = new TailManager(skinLookup, view);
    }

    /**
     * Initializes the drag manager for the given model.
     *
     * @param model
     *            the {@link GModel} currently being edited
     */
    public void initialize(final GModel model)
    {
        this.model = model;
        clearTrackingParameters();
        setHandlers();
    }

    /**
     * Sets the validator that determines what connections can be created.
     *
     * @param validator
     *            a {@link GConnectorValidator} implementation, or null to use
     *            the default
     */
    public void setValidator(final GConnectorValidator validator)
    {
        this.validator = Objects.requireNonNullElseGet(validator, DefaultConnectorValidator::new);
    }

    /**
     * Clears all parameters that track things like what connector is currently
     * hovered over, and so on.
     */
    private void clearTrackingParameters()
    {
        tailManager.cleanUp();
        hoveredConnector = null;
        removalConnector = null;
        repositionAllowed = true;
    }

    public void addConnector(final GConnector connector)
    {
        addMouseHandlers(connector);
    }

    public void removeConnector(final GConnector pConnectorToRemove)
    {
        final GConnectorSkin connectorSkin = skinLookup.lookupConnector(pConnectorToRemove);
        if (connectorSkin != null)
        {
            final Node root = connectorSkin.getRoot();
            if (root != null)
            {
                removeSingleEventHandler(root, mouseEnteredHandlers, MouseEvent.MOUSE_ENTERED);
                removeSingleEventHandler(root, mouseReleasedHandlers, MouseEvent.MOUSE_RELEASED);
                removeSingleEventHandler(root, dragDetectedHandlers, MouseEvent.DRAG_DETECTED);
                removeSingleEventHandler(root, mouseDraggedHandlers, MouseEvent.MOUSE_DRAGGED);
                removeSingleEventHandler(root, mouseDragEnteredHandlers, MouseDragEvent.MOUSE_DRAG_ENTERED);
                removeSingleEventHandler(root, mouseDragExitedHandlers, MouseDragEvent.MOUSE_DRAG_EXITED);
                removeSingleEventHandler(root, mouseDragReleasedHandlers, MouseDragEvent.MOUSE_DRAG_RELEASED);

                removeGeneralEventHandlers(root);
            }
        }

        // the connector's tail we are dragging around has been removed..
        if (sourceConnector == pConnectorToRemove || targetConnector == pConnectorToRemove || removalConnector == pConnectorToRemove)
        {
            clearTrackingParameters();
        }
    }

    private void removeGeneralEventHandlers(final Node node)
    {
        node.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedHandler);
        node.removeEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
    }

    private static <T extends Event> void removeSingleEventHandler(final Node node, final Map<Node, EventHandler<T>> eventHandlerMap,
            final EventType<T> eventType)
    {
        final EventHandler<T> handler = eventHandlerMap.remove(node);
        if (handler != null)
        {
            node.removeEventHandler(eventType, handler);
        }
    }

    /**
     * Sets all mouse and mouse-drag handlers for all connectors in the current
     * model.
     */
    private void setHandlers()
    {
        // here we assume that all event handler maps are of exactly the same size
        for (final Node node : mouseEnteredHandlers.keySet())
        {
            removeGeneralEventHandlers(node);
        }

        EventUtils.removeEventHandlers(mouseEnteredHandlers, MouseEvent.MOUSE_ENTERED);
        EventUtils.removeEventHandlers(mouseReleasedHandlers, MouseEvent.MOUSE_RELEASED);
        EventUtils.removeEventHandlers(dragDetectedHandlers, MouseEvent.DRAG_DETECTED);
        EventUtils.removeEventHandlers(mouseDraggedHandlers, MouseEvent.MOUSE_DRAGGED);
        EventUtils.removeEventHandlers(mouseDragEnteredHandlers, MouseDragEvent.MOUSE_DRAG_ENTERED);
        EventUtils.removeEventHandlers(mouseDragExitedHandlers, MouseDragEvent.MOUSE_DRAG_EXITED);
        EventUtils.removeEventHandlers(mouseDragReleasedHandlers, MouseDragEvent.MOUSE_DRAG_RELEASED);

        for (final GNode node : model.getNodes())
        {
            for (final GConnector connector : node.getConnectors())
            {
                addMouseHandlers(connector);
            }
        }
    }

    /**
     * Adds mouse handlers to a particular connector.
     *
     * @param connector
     *            the {@link GConnector} to which mouse handlers should be added
     */
    private void addMouseHandlers(final GConnector connector)
    {
        final GConnectorSkin connectorSkin = skinLookup.lookupConnector(connector);
        if (connectorSkin != null)
        {
            final Node root = skinLookup.lookupConnector(connector).getRoot();
            if (root == null || mouseEnteredHandlers.containsKey(root))
            {
                return;
            }

            final EventHandler<MouseEvent> newMouseEnteredHandler = event -> handleMouseEntered(event, connector);
            final EventHandler<MouseEvent> newMouseReleasedHandler = this::handleMouseReleased;

            final EventHandler<MouseEvent> newDragDetectedHandler = event -> handleDragDetected(event, connectorSkin);
            final EventHandler<MouseEvent> newMouseDraggedHandler = event -> handleMouseDragged(event, connector);
            final EventHandler<MouseDragEvent> newMouseDragEnteredHandler =
                    event -> handleDragEntered(event, connectorSkin);
            final EventHandler<MouseDragEvent> newMouseDragExitedHandler =
                    event -> handleDragExited(event, connectorSkin);
            final EventHandler<MouseDragEvent> newMouseDragReleasedHandler =
                    event -> handleDragReleased(event, connectorSkin);

            root.addEventHandler(MouseEvent.MOUSE_ENTERED, newMouseEnteredHandler);
            root.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
            root.addEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedHandler);
            root.addEventHandler(MouseEvent.MOUSE_RELEASED, newMouseReleasedHandler);

            root.addEventHandler(MouseEvent.DRAG_DETECTED, newDragDetectedHandler);
            root.addEventHandler(MouseEvent.MOUSE_DRAGGED, newMouseDraggedHandler);
            root.addEventHandler(MouseDragEvent.MOUSE_DRAG_ENTERED, newMouseDragEnteredHandler);
            root.addEventHandler(MouseDragEvent.MOUSE_DRAG_EXITED, newMouseDragExitedHandler);
            root.addEventHandler(MouseDragEvent.MOUSE_DRAG_RELEASED, newMouseDragReleasedHandler);

            mouseEnteredHandlers.put(root, newMouseEnteredHandler);
            mouseReleasedHandlers.put(root, newMouseReleasedHandler);

            dragDetectedHandlers.put(root, newDragDetectedHandler);
            mouseDraggedHandlers.put(root, newMouseDraggedHandler);
            mouseDragEnteredHandlers.put(root, newMouseDragEnteredHandler);
            mouseDragExitedHandlers.put(root, newMouseDragExitedHandler);
            mouseDragReleasedHandlers.put(root, newMouseDragReleasedHandler);
        }
    }

    /**
     * Handles mouse-entered events on the given connector.
     *
     * @param event
     *            a mouse-entered event
     * @param connector
     *            the {@link GConnector} on which this event occurred
     */
    private void handleMouseEntered(final MouseEvent event, final GConnector connector)
    {
        hoveredConnector = connector;
        event.consume();
    }

    /**
     * Handles mouse-exited events on the given connector.
     *
     * @param event
     *            a mouse-exited event
     */
    private void handleMouseExited(final MouseEvent event)
    {
        hoveredConnector = null;
        event.consume();
    }


    /**
     * Handles mouse-released events on the given connector.
     *
     * @param event
     *            a mouse-released event
     */
    private void handleMouseReleased(final MouseEvent event)
    {
        final GConnectorSkin targetConnectorSkin;
        if (targetConnector != null && (targetConnectorSkin = skinLookup.lookupConnector(targetConnector)) != null)
        {
            targetConnectorSkin.applyStyle(GConnectorStyle.DEFAULT);
        }

        sourceConnector = null;
        removalConnector = null;
        repositionAllowed = true;

        tailManager.cleanUp();
        finishGesture();

        event.consume();
    }

    /**
     * Handles drag-detected events on the given connector.
     *
     * @param pEvent
     *            a drag-detected event
     * @param connectorSkin
     *            the {@link GConnectorSkin} on which this event occurred
     */
    private void handleDragDetected(final MouseEvent pEvent, final GConnectorSkin connectorSkin)
    {
        if (pEvent.getButton() != MouseButton.PRIMARY)
        {
            return;
        }

        final GConnector connector = connectorSkin.getItem();
        if (checkCreatable(connector) && activateGesture(pEvent))
        {
            sourceConnector = connector;
            connectorSkin.getRoot().startFullDrag();
            tailManager.cleanUp();
            tailManager.create(connector, pEvent);
        }
        else if (checkRemovable(connector) && activateGesture(pEvent))
        {
            removalConnector = connector;
            connectorSkin.getRoot().startFullDrag();
        }

        pEvent.consume();
    }

    /**
     * Handles mouse-dragged events on the given connector.
     *
     * @param event
     *            a mouse-dragged event
     * @param connector
     *            the {@link GConnector} on which this event occurred
     */
    private void handleMouseDragged(final MouseEvent event, final GConnector connector)
    {
        if (repositionAllowed && activateGesture(event))
        {
            // Case for when the mouse first exits a connector during a drag gesture.
            if (removalConnector != null && !removalConnector.equals(hoveredConnector))
            {
                detachConnection(event, connector);
            }
            else
            {
                tailManager.updatePosition(event);
            }
            event.consume();
        }
    }

    /**
     * Handles drag-entered events on the given connector.
     *
     * @param event
     *            a drag-entered event
     * @param connectorSkin
     *            the {@link GConnectorSkin} on which this event occurred
     */
    private void handleDragEntered(final MouseEvent event, final GConnectorSkin connectorSkin)
    {
        if (!activateGesture(event))
        {
            return;
        }

        final GConnector connector = connectorSkin.getItem();
        if (validator.prevalidate(sourceConnector, connector))
        {
            final boolean valid = validator.validate(sourceConnector, connector);
            tailManager.snapPosition(sourceConnector, connector, valid);

            repositionAllowed = false;

            if (valid)
            {
                connectorSkin.applyStyle(GConnectorStyle.DRAG_OVER_ALLOWED);
            }
            else
            {
                connectorSkin.applyStyle(GConnectorStyle.DRAG_OVER_FORBIDDEN);
            }
        }

        event.consume();
    }

    /**
     * Handles drag-exited events on the given connector.
     *
     * @param event
     *            a drag-exited event
     * @param connectorSkin
     *            the {@link GConnectorSkin} on which this event occurred
     */
    private void handleDragExited(final MouseEvent event, final GConnectorSkin connectorSkin)
    {
        connectorSkin.applyStyle(GConnectorStyle.DEFAULT);
        repositionAllowed = true;

        tailManager.updatePosition(event);

        event.consume();
    }

    /**
     * Handles drag-released events on the given connector.
     *
     * @param event
     *            a drag-released event
     * @param connectorSkin
     *            the {@link GConnectorSkin} on which this event occurred
     */
    private void handleDragReleased(final MouseEvent event, final GConnectorSkin connectorSkin)
    {
        if (event.isConsumed())
        {
            return;
        }

        // Consume the event now so it doesn't fire repeatedly after re-initialization.
        event.consume();

        final GConnector connector = connectorSkin.getItem();
        if (validator.prevalidate(sourceConnector, connector) && validator.validate(sourceConnector, connector))
        {
            addConnection(sourceConnector, connector);
        }

        connectorSkin.applyStyle(GConnectorStyle.DEFAULT);
        tailManager.cleanUp();
        finishGesture();
    }

    /**
     * Checks if a connection can be created from the given connector.
     *
     * @param connector
     *            a {@link GConnector} instance
     * @return {@code true} if a connection can be created from the given
     *         {@link GConnector}, {@code false} if not
     */
    private boolean checkCreatable(final GConnector connector)
    {
        return connector != null && connector.eContainer() instanceof GNode && checkEditable()
                && (connector.getConnections().isEmpty() || !connector.isConnectionDetachedOnDrag());
    }

    /**
     * Checks if a connection can be removed from the given connector.
     *
     * @param connector
     *            a {@link GConnector} instance
     * @return {@code true} if a connection can be removed from the given
     *         {@link GConnector}, {@code false} if not
     */
    private boolean checkRemovable(final GConnector connector)
    {
        return checkEditable() && !connector.getConnections().isEmpty() && connector.isConnectionDetachedOnDrag();
    }

    private boolean checkEditable()
    {
        return getEditorProperties() != null && !getEditorProperties().isReadOnly(EditorElement.CONNECTOR);
    }

    private GraphEditorProperties getEditorProperties()
    {
        return view == null ? null : view.getEditorProperties();
    }

    /**
     * Adds a new connection to the model.
     *
     * <p>
     * This will trigger the model listener and cause everything to be
     * reinitialized.
     * </p>
     *
     * @param source
     *            the source {@link GConnector} for the new connection
     * @param target
     *            the target {@link GConnector} for the new connection
     */
    private void addConnection(final GConnector source, final GConnector target)
    {
        final String connectionType = validator.createConnectionType(source, target);
        final String jointType = validator.createJointType(source, target);
        final List<Point2D> jointPositions = skinLookup.lookupTail(source).allocateJointPositions();

        final List<GJoint> joints = new ArrayList<>();

        for (final Point2D position : jointPositions)
        {
            final GJoint joint = GraphFactory.eINSTANCE.createGJoint();
            joint.setX(position.getX());
            joint.setY(position.getY());
            joint.setType(jointType);

            joints.add(joint);
        }

        ConnectionCommands.addConnection(model, source, target, connectionType, joints, connectionEventManager);
    }

    /**
     * Detaches the first connection from the given connector - i.e. removes the
     * connection and replaces it with a tail.
     *
     * @param event
     *            the {@link MouseEvent} that caused the connection to be
     *            detached
     * @param connector
     *            the connector that the connection was detached from
     */
    private void detachConnection(final MouseEvent event, final GConnector connector)
    {
        final GConnectorSkin connectorSkin = skinLookup.lookupConnector(connector);
        final int connectorCount = getConnectorCount(connector);
        if (connectorSkin != null)
        {
            connectorSkin.applyStyle(GConnectorStyle.DEFAULT);
        }

        if (connector.getConnections().isEmpty())
        {
            return;
        }

        boolean followUpCreated = false;

        final GConnection[] connections = connector.getConnections().toArray(new GConnection[0]);
        for (final GConnection connection : connections)
        {
            if (skinLookup.lookupConnection(connection) instanceof VirtualSkin)
            {
                // do not touch virtual connections
                continue;
            }
            final GConnector opposingConnector = getOpposingConnector(connection, connector);
            final List<Point2D> jointPositions = GeometryUtils.getJointPositions(connection, skinLookup);
            final GConnector newSource;
            if (connector.equals(connection.getSource()))
            {
                Collections.reverse(jointPositions);
                newSource = connection.getTarget();
            }
            else
            {
                newSource = connection.getSource();
            }

            ConnectionCommands.removeConnection(model, connection, connectionEventManager);

            final GConnectorSkin updateConnectorSkin;
            if ((updateConnectorSkin = skinLookup.lookupConnector(connector)) == null || updateConnectorSkin != connectorSkin
                    || connectorCount != getConnectorCount(connector))
            {
                // business logic decided to remove this connection or structurally change the parent node
                continue;
            }

            // check if the new source connector allows to create a new connection on the fly:
            if (!followUpCreated && checkCreatable(opposingConnector))
            {
                tailManager.updateToNewSource(jointPositions, newSource, event);
                sourceConnector = opposingConnector;
                targetConnector = connector;
                followUpCreated = true;
            }
        }

        // no follow up tail created -> clean up
        if (!followUpCreated)
        {
            clearTrackingParameters();
            sourceConnector = null;
            targetConnector = null;
            finishGesture();
        }

        removalConnector = null;
    }

    private static int getConnectorCount(final GConnector pConnector)
    {
        return pConnector == null || pConnector.getParent() == null ? 1 : pConnector.getParent().getConnectors().size();
    }

    private GConnector getOpposingConnector(final GConnection pConnection, final GConnector pConnector)
    {
        if (!pConnection.getSource().equals(pConnector))
        {
            return pConnection.getSource();
        }
        else
        {
            return pConnection.getTarget();
        }
    }

    private boolean activateGesture(final Event pEvent)
    {
        final GraphEventManager eventManager = getEditorProperties();
        if (eventManager != null)
        {
            eventManager.activateGesture(GraphInputGesture.CONNECT, pEvent, this);
        }
        return true;
    }

    private void finishGesture()
    {
        final GraphEventManager eventManager = getEditorProperties();
        if (eventManager != null)
        {
            eventManager.finishGesture(GraphInputGesture.CONNECT, this);
        }
    }
} public class DefaultModelEditingManager implements ModelEditingManager
{

    private static final URI DEFAULT_URI = URI.createFileURI("");

    private final CommandStackListener commandStackListener;

    private EditingDomain editingDomain;
    private GModel model;

    private BiFunction<RemoveContext, GConnection, Command> mOnConnectionRemoved;
    private BiFunction<RemoveContext, GNode, Command> mOnNodeRemoved;

    /**
     * Creates a new model editing manager. Only one instance should exist per
     * {@link DefaultGraphEditor} instance.
     *
     * @param pCommandStackListener
     *            the {@link CommandStackListener} that listens for changes in
     *            the model
     */
    public DefaultModelEditingManager(final CommandStackListener pCommandStackListener)
    {
        commandStackListener = pCommandStackListener;
    }

    @Override
    public void initialize(final GModel pModel)
    {
        // Only initialize the editing domain if the model object has actually changed.
        if (!pModel.equals(model))
        {
            initializeEditingDomain(model, pModel);
        }
        model = pModel;
    }

    @Override
    public void setOnConnectionRemoved(final BiFunction<RemoveContext, GConnection, Command> pOnConnectionRemoved)
    {
        mOnConnectionRemoved = pOnConnectionRemoved;
    }

    @Override
    public void setOnNodeRemoved(final BiFunction<RemoveContext, GNode, Command> pOnNodeRemoved)
    {
        mOnNodeRemoved = pOnNodeRemoved;
    }

    @Override
    public void updateLayoutValues(final SkinLookup skinLookup)
    {
        final CompoundCommand command = new CompoundCommand();

        Commands.updateLayoutValues(command, model, skinLookup);

        editingDomain.getCommandStack().removeCommandStackListener(commandStackListener);

        if (command.canExecute())
        {
            editingDomain.getCommandStack().execute(command);
        }

        editingDomain.getCommandStack().addCommandStackListener(commandStackListener);
    }

    @Override
    public void remove(final Collection<EObject> pToRemove)
    {
        if (pToRemove == null || pToRemove.isEmpty())
        {
            return;
        }

        final CompoundCommand command = new CompoundCommand();
        final RemoveContext editContext = new RemoveContext();
        final List<EObject> delete = new ArrayList<>(pToRemove.size());

        // pre-fill the RemoveContext with all elements to be removed:
        for (final EObject obj : pToRemove)
        {
            if (obj instanceof GNode n && editContext.canRemove(obj))
            {
                delete.add(obj);
                for (final GConnector connector : n.getConnectors())
                {
                    for (final GConnection connection : connector.getConnections())
                    {
                        if (connection != null && editContext.canRemove(connection))
                        {
                            delete.add(connection);
                        }
                    }
                }
            }
            else if (obj instanceof GConnection && editContext.canRemove(obj))
            {
                delete.add(obj);
            }
        }

        // delete the elements and call business logic add-ins:
        for (final EObject obj : delete)
        {
            if (obj instanceof GNode)
            {
                command.append(RemoveCommand.create(editingDomain, model, GraphPackage.Literals.GMODEL__NODES, obj));

                final Command onRemoved = mOnNodeRemoved == null ? null : mOnNodeRemoved.apply(editContext, (GNode) obj);
                if (onRemoved != null)
                {
                    command.append(onRemoved);
                }
            }
            else if (obj instanceof GConnection)
            {
                remove(editContext, command, (GConnection) obj);
            }
        }

        if (!command.isEmpty() && command.canExecute())
        {
            editingDomain.getCommandStack().execute(command);
        }
    }

    private void remove(final RemoveContext pRemoveContext, final CompoundCommand pCommand, final GConnection pToDelete)
    {
        final GConnector source = pToDelete.getSource();
        final GConnector target = pToDelete.getTarget();

        pCommand.append(RemoveCommand.create(editingDomain, model, GraphPackage.Literals.GMODEL__CONNECTIONS, pToDelete));
        pCommand.append(RemoveCommand.create(editingDomain, source, GraphPackage.Literals.GCONNECTOR__CONNECTIONS, pToDelete));
        pCommand.append(RemoveCommand.create(editingDomain, target, GraphPackage.Literals.GCONNECTOR__CONNECTIONS, pToDelete));

        final Command onRemoved = mOnConnectionRemoved == null ? null : mOnConnectionRemoved.apply(pRemoveContext, pToDelete);
        if (onRemoved != null)
        {
            pCommand.append(onRemoved);
        }
    }

    /**
     * Initializes the editing domain and resource for the new model.
     *
     * <p>
     * If a resource and/or editing domain are already associated to this model,
     * these will be used. Otherwise they will be created.
     * </p>
     */
    private void initializeEditingDomain(final GModel oldModel, final GModel newModel)
    {
        // First remove the listener from the old model, if it exists.
        if (oldModel != null)
        {
            final EditingDomain oldDomain = AdapterFactoryEditingDomain.getEditingDomainFor(oldModel);
            if (oldDomain != null)
            {
                oldDomain.getCommandStack().removeCommandStackListener(commandStackListener);
            }
        }

        if (newModel.eResource() == null)
        {
            final XMIResourceFactoryImpl resourceFactory = new XMIResourceFactoryImpl();
            final Resource resource = resourceFactory.createResource(DEFAULT_URI);
            resource.getContents().add(newModel);
        }

        editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(newModel);

        if (editingDomain == null)
        {
            final Registry registry = ComposedAdapterFactory.Descriptor.Registry.INSTANCE;
            final AdapterFactory adapterFactory = new ComposedAdapterFactory(registry);

            editingDomain = new AdapterFactoryEditingDomain(adapterFactory, new BasicCommandStack());
            editingDomain.getResourceSet().getResources().add(newModel.eResource());
        }

        editingDomain.getCommandStack().addCommandStackListener(commandStackListener);
    }
}
 public class ModelLayoutUpdater
{

    private final SkinLookup skinLookup;
    private final ModelEditingManager modelEditingManager;
    private final GraphEditorProperties properties;
    private final EventHandler<MouseEvent> mouseReleasedHandlerNode = event -> elementMouseReleased(EditorElement.NODE);
    private final EventHandler<MouseEvent> mouseReleasedHandlerJoint = event -> elementMouseReleased(EditorElement.JOINT);

    /**
     * Creates a new model layout updater. Only one instance should exist per
     * graph editor instance.
     *
     * @param pSkinLookup
     *            the {@link SkinLookup} used to lookup skins
     * @param pModelEditingManager
     *            the {@link ModelEditingManager} used to update the model
     *            values
     */
    public ModelLayoutUpdater(final SkinLookup pSkinLookup, final ModelEditingManager pModelEditingManager,
            final GraphEditorProperties pProperties)
    {
        skinLookup = pSkinLookup;
        modelEditingManager = pModelEditingManager;
        properties = pProperties;
    }

    /**
     * Adds a handler to update the model when a node's layout properties
     * change.
     *
     * @param node
     *            the {@link GNode} whose values should be updated
     */
    public void addNode(final GNode node)
    {
        final GNodeSkin nodeSkin = skinLookup.lookupNode(node);
        if (nodeSkin != null)
        {
            final Node root = nodeSkin.getRoot();
            if (root != null)
            {
                root.addEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleasedHandlerNode);
            }
        }
    }

    public void removeNode(final GNode node)
    {
        final GNodeSkin nodeSkin = skinLookup.lookupNode(node);
        if (nodeSkin != null)
        {
            final Node root = nodeSkin.getRoot();
            if (root != null)
            {
                root.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleasedHandlerNode);
            }
        }
    }

    /**
     * Adds a handler to update the model when a joint's layout properties
     * change.
     *
     * @param joint
     *            the {@link GJoint} whose values should be updated
     */
    public void addJoint(final GJoint joint)
    {
        final GJointSkin jointSkin = skinLookup.lookupJoint(joint);
        if (jointSkin != null)
        {
            final Node root = jointSkin.getRoot();
            if (root != null)
            {
                root.addEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleasedHandlerJoint);
            }
        }
    }

    public void removeJoint(final GJoint joint)
    {
        final GJointSkin jointSkin = skinLookup.lookupJoint(joint);
        if (jointSkin != null)
        {
            final Node root = jointSkin.getRoot();
            if (root != null)
            {
                root.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleasedHandlerJoint);
            }
        }
    }

    private void elementMouseReleased(final EditorElement pType)
    {
        if (canEdit(pType))
        {
            modelEditingManager.updateLayoutValues(skinLookup);
        }
    }

    private boolean canEdit(final EditorElement pType)
    {
        final GraphEditorProperties props = properties;
        return props != null && !props.isReadOnly(pType);
    }
} public final class ModelSanityChecker
{

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultGraphEditor.class);

    /**
     * Static class, private constructor.
     */
    private ModelSanityChecker()
    {
    }

    /**
     * Validates the given {@link GModel}.
     *
     * @param model
     *            the {@link GModel} to be validated
     * @return {@code true} if the model is valid
     */
    public static boolean validate(final GModel model)
    {
        return validateSizes(model) && validateReferences(model);
    }

    /**
     * Performs a basic sanity check that width and height parameters are
     * non-negative.
     *
     * @param model
     *            the {@link GModel} to be validated
     * @return {@code true} if the model width and height parameters are valid
     */
    private static boolean validateSizes(final GModel model)
    {
        if (model.getContentWidth() < 0 || model.getContentHeight() < 0)
        {
            LOGGER.error("Model contains negative width / height values.");
            return false;
        }

        for (final GNode node : model.getNodes())
        {
            if (node.getWidth() < 0 || node.getHeight() < 0)
            {
                LOGGER.error("Model contains negative width / height values.");
                return false;
            }
        }

        return true;
    }

    /**
     * Validates that the references between connectors and their connections
     * make sense.
     *
     * @param model
     *            the {@link GModel} to be validated
     * @return {@code true} if the model references are valid
     */
    private static boolean validateReferences(final GModel model)
    {
        boolean valid = true;

        for (final GConnection connection : model.getConnections())
        {

            final GConnector source = connection.getSource();
            final GConnector target = connection.getTarget();

            if (source == null || target == null)
            {
                LOGGER.error("Connection must have non-null source and target connectors.");
                valid = false;
            }
            else if (source != null && !source.getConnections().contains(connection))
            {
                LOGGER.error("A connector is missing a reference to its connection.");
                valid = false;
            }
            else if (target != null && !target.getConnections().contains(connection))
            {
                LOGGER.error("A connector is missing a reference to its connection.");
                valid = false;
            }
        }

        return valid;
    }
} public class DefaultSelectionManager implements SelectionManager
{

    private final SelectionCreator selectionCreator;
    private final SelectionTracker selectionTracker;

    private GModel model;

    /**
     * Creates a new default selection manager. Only one instance should exist
     * per {@link DefaultGraphEditor} instance.
     *
     * @param skinLookup
     *            the {@link SkinLookup} instance in use
     * @param view
     *            the {@link GraphEditorView} instance in use
     */
    public DefaultSelectionManager(final SkinLookup skinLookup, final GraphEditorView view)
    {
        final SelectionDragManager selectionDragManager = new SelectionDragManager(skinLookup, view, this);
        selectionCreator = new SelectionCreator(skinLookup, view, this, selectionDragManager);
        selectionTracker = new SelectionTracker(skinLookup);
    }

    /**
     * Initializes the selection manager for the given model.
     *
     * @param model
     *            the {@link GModel} currently being edited
     */
    public void initialize(final GModel model)
    {
        this.model = model;

        selectionCreator.initialize(model);
        selectionTracker.initialize();
    }

    public void addNode(final GNode node)
    {
        selectionCreator.addNode(node);
    }

    public void removeNode(final GNode node)
    {
        selectionCreator.removeNode(node);
    }

    public void addConnector(final GConnector connector)
    {
        selectionCreator.addConnector(connector);
    }

    public void removeConnector(final GConnector connector)
    {
        selectionCreator.removeConnector(connector);
    }

    public void addConnection(final GConnection connection)
    {
        selectionCreator.addConnection(connection);
    }

    public void removeConnection(final GConnection connection)
    {
        selectionCreator.removeConnection(connection);
    }

    public void addJoint(final GJoint joint)
    {
        selectionCreator.addJoint(joint);
    }

    public void removeJoint(final GJoint joint)
    {
        selectionCreator.removeJoint(joint);
    }

    @Override
    public ObservableSet<EObject> getSelectedItems()
    {
        return selectionTracker.getSelectedItems();
    }

    @Override
    public void select(final EObject object)
    {
        getSelectedItems().add(object);
    }

    @Override
    public void clearSelection(final EObject object)
    {
        getSelectedItems().remove(object);
    }

    @Override
    public boolean isSelected(EObject object)
    {
        return getSelectedItems().contains(object);
    }

    @Override
    public List<GNode> getSelectedNodes()
    {
        return selectionTracker.getSelectedNodes();
    }

    @Override
    public List<GConnection> getSelectedConnections()
    {
        return selectionTracker.getSelectedConnections();
    }

    @Override
    public List<GJoint> getSelectedJoints()
    {
        return selectionTracker.getSelectedJoints();
    }

    @Override
    public void clearSelection()
    {
        if (!getSelectedItems().isEmpty())
        {
            // copy to prevent ConcurrentModificationException
            // (removal triggers update notification which in turn could modify the selection)
            final EObject[] selectedItems = getSelectedItems().toArray(new EObject[0]);
            for (final EObject remove : selectedItems)
            {
                getSelectedItems().remove(remove);
            }
        }
    }

    @Override
    public void selectAll()
    {
        if (model != null)
        {
            getSelectedItems().addAll(model.getNodes());
            for (final GConnection connection : model.getConnections())
            {
                getSelectedItems().add(connection);

                for (final GJoint joint : connection.getJoints())
                {
                    getSelectedItems().add(joint);
                }
            }
        }
    }
} public interface SkinManager extends SkinLookup, GraphEditorSkins
{

    /**
     * @param pConnectionLayouter
     *            {@link ConnectionLayouter}
     * @since 16.01.2019
     */
    void setConnectionLayouter(final ConnectionLayouter pConnectionLayouter);

    /**
     * remove all cached skins and clear the graph editor view
     */
    void clear();

    /**
     * Removes the given {@link GNode} skin from the view
     *
     * @param pNodeToRemove
     *            node to remove
     * @since 10.02.2016
     */
    void removeNode(final GNode pNodeToRemove);

    /**
     * Removes the given {@link GConnector} skin from the view
     *
     * @param pConnectorToRemove
     *            connector to remove
     * @since 10.02.2016
     */
    void removeConnector(final GConnector pConnectorToRemove);

    /**
     * Removes the given {@link GConnection} skin from the view
     *
     * @param pConnectionToRemove
     *            connection to remove
     * @since 10.02.2016
     */
    void removeConnection(final GConnection pConnectionToRemove);

    /**
     * Removes the given {@link GJoint} skin from the view
     *
     * @param pJointToRemove
     *            joint to remove
     * @since 10.02.2016
     */
    void removeJoint(final GJoint pJointToRemove);
    /**
     * Calls {@link GNodeSkin#setConnectorSkins(List)} to update a nodes list of
     * connectors.
     *
     * @param pNode
     *            node to update
     * @since 10.02.2016
     */
    void updateConnectors(final GNode pNode);

    /**
     * Calls {@link GConnectionSkin#setJointSkins(List)} to update a connections
     * list of joints.
     *
     * @param pConnection
     *            connection to update
     * @since 17.02.2016
     */
    void updateJoints(final GConnection pConnection);

    /**
     * Creates (if not yet existing) and returns the skin for the given item
     *
     * @param pNode
     * @return skin
     * @since 21.01.2019
     */
    GNodeSkin lookupOrCreateNode(final GNode pNode);

    /**
     * Creates (if not yet existing) and returns the skin for the given item
     *
     * @param pConnector
     * @return skin
     * @since 21.01.2019
     */
    GConnectorSkin lookupOrCreateConnector(final GConnector pConnector);

    /**
     * Creates (if not yet existing) and returns the skin for the given item
     *
     * @param pConnection
     * @return skin
     * @since 21.01.2019
     */
    GConnectionSkin lookupOrCreateConnection(final GConnection pConnection);

    /**
     * Creates (if not yet existing) and returns the skin for the given item
     *
     * @param pJoint
     * @return skin
     * @since 21.01.2019
     */
    GJointSkin lookupOrCreateJoint(final GJoint pJoint);
} public interface ConnectionLayouter
{

    /**
     * Initializes the connection layout manager for the given model.
     *
     * @param pModel
     *            the {@link GModel} currently being edited
     */
    void initialize(final GModel pModel);

    /**
     * Draws all connections according to the latest layout values.
     */
    void draw();

    /**
     * Marks the complete connection layout as dirty. The next layout pass will
     * re-layout all connections (during {@link #draw()}).
     *
     * @since 31.01.2019
     */
    void redrawAll();

    /**
     * Redraw the given connection according to the latest layout values.
     *
     * @param pConnection
     *            connection to redraw
     */
    void redraw(final GConnection pConnection);

    /**
     * Redraw the given connections according to the latest layout values.
     *
     * @param pConnections
     *            connections to redraw
     */
    void redraw(final Collection<GConnection> pConnections);
} public class GraphEditorView extends Region
{

    private static final String STYLE_CLASS = "graph-editor";
    private static final String STYLE_CLASS_NODE_LAYER = "graph-editor-node-layer";
    private static final String STYLE_CLASS_CONNECTION_LAYER = "graph-editor-connection-layer";

    private final Pane mNodeLayer = new Pane();

    private final Pane mConnectionLayer = new Pane()
    {

        @Override
        protected void layoutChildren()
        {
            super.layoutChildren();
            drawConnections();
        }
    };

    private final GraphEditorGrid mGrid = new GraphEditorGrid();
    private ConnectionLayouter mConnectionLayouter;

    private final SelectionBox mSelectionBox = new SelectionBox();

    private final GraphEditorProperties mEditorProperties;

    /**
     * Creates a new {@link GraphEditorView} to which skin instances can be
     * added and removed.
     */
    public GraphEditorView(final GraphEditorProperties pEditorProperties)
    {
        getStyleClass().addAll(STYLE_CLASS);

        setMaxWidth(GraphEditorProperties.DEFAULT_MAX_WIDTH);
        setMaxHeight(GraphEditorProperties.DEFAULT_MAX_HEIGHT);

        initializeLayers();

        mEditorProperties = pEditorProperties;

        if (mEditorProperties != null)
        {
            mGrid.visibleProperty().bind(mEditorProperties.gridVisibleProperty());
            mGrid.gridSpacingProperty().bind(mEditorProperties.gridSpacingProperty());
        }
    }

    /**
     * Sets the connection-layouter to be used by the view.
     *
     * @param pConnectionLayouter
     *            the graph editor's {@link ConnectionLayouter} instance
     */
    public void setConnectionLayouter(final ConnectionLayouter pConnectionLayouter)
    {
        mConnectionLayouter = pConnectionLayouter;
    }

    /**
     * Clears all elements from the view.
     */
    public void clear()
    {
        mNodeLayer.getChildren().clear();
        mConnectionLayer.getChildren().clear();
    }

    /**
     * Adds a node skin to the view.
     *
     * @param pNodeSkin
     *            the {@link GNodeSkin} instance to be added
     */
    public void add(final GNodeSkin pNodeSkin)
    {
        if (pNodeSkin != null && !(pNodeSkin instanceof VirtualSkin))
        {
            mNodeLayer.getChildren().add(pNodeSkin.getRoot());
        }
    }

    /**
     * Adds a connection skin to the view.
     *
     * @param pConnectionSkin
     *            the {@link GConnectionSkin} instance to be added
     */
    public void add(final GConnectionSkin pConnectionSkin)
    {
        if (pConnectionSkin != null && !(pConnectionSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().add(0, pConnectionSkin.getRoot());
        }
    }

    /**
     * Adds a joint skin to the view.
     *
     * @param pJointSkin
     *            the {@link GJointSkin} instance to be added
     */
    public void add(final GJointSkin pJointSkin)
    {
        if (pJointSkin != null && !(pJointSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().add(pJointSkin.getRoot());
        }
    }

    /**
     * Adds a tail skin to the view.
     *
     * @param pTailSkin
     *            the {@link GTailSkin} instance to be added
     */
    public void add(final GTailSkin pTailSkin)
    {
        if (pTailSkin != null && !(pTailSkin instanceof VirtualSkin))
        {
            // add to back:
            mConnectionLayer.getChildren().add(0, pTailSkin.getRoot());
        }
    }

    /**
     * Removes a node skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pNodeSkin
     *            the {@link GNodeSkin} instance to remove
     */
    public void remove(final GNodeSkin pNodeSkin)
    {
        if (pNodeSkin != null && !(pNodeSkin instanceof VirtualSkin))
        {
            mNodeLayer.getChildren().remove(pNodeSkin.getRoot());
        }
    }

    /**
     * Removes a connection skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pConnectionSkin
     *            the {@link GConnectionSkin} instance to remove
     */
    public void remove(final GConnectionSkin pConnectionSkin)
    {
        if (pConnectionSkin != null && !(pConnectionSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pConnectionSkin.getRoot());
        }
    }

    /**
     * Removes a joint skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pJointSkin
     *            the {@link GJointSkin} instance to remove
     */
    public void remove(final GJointSkin pJointSkin)
    {
        if (pJointSkin != null && !(pJointSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pJointSkin.getRoot());
        }
    }

    /**
     * Removes a tail skin from the view. Does nothing if the skin is not
     * present.
     *
     * @param pTailSkin
     *            the {@link GTailSkin} instance to remove
     */
    public void remove(final GTailSkin pTailSkin)
    {
        if (pTailSkin != null && !(pTailSkin instanceof VirtualSkin))
        {
            mConnectionLayer.getChildren().remove(pTailSkin.getRoot());
        }
    }

    /**
     * Gets the editor properties instance used by the view.
     *
     * @return editorProperties the {@link GraphEditorProperties} instance used
     *         by the view
     */
    public GraphEditorProperties getEditorProperties()
    {
        return mEditorProperties;
    }

    /**
     * Draws a selection box in the view.
     *
     * @param pX
     *            the x position of the selection box
     * @param pY
     *            the y position of the selection box
     * @param pWidth
     *            the width of the selection box
     * @param pHeight
     *            the height of the selection box
     */
    public void drawSelectionBox(final double pX, final double pY, final double pWidth, final double pHeight)
    {
        mSelectionBox.draw(pX, pY, pWidth, pHeight);
    }

    /**
     * Hides the selection box.
     */
    public void hideSelectionBox()
    {
        mSelectionBox.setVisible(false);
    }

    @Override
    protected void layoutChildren()
    {
        final double width = getWidth();
        final double height = getHeight();
        mNodeLayer.resizeRelocate(0, 0, width, height);
        mConnectionLayer.resizeRelocate(0, 0, width, height);
        mGrid.resizeRelocate(0, 0, width, height);
        drawConnections();
    }

    /**
     * calls {@link ConnectionLayouter#draw()}
     *
     * @since 31.01.2019
     */
    void drawConnections()
    {
        if (mConnectionLayouter != null)
        {
            mConnectionLayouter.draw();
        }
    }

    /**
     * Initializes the two layers (node and connection) that the view is
     * composed of.
     */
    private void initializeLayers()
    {
        mNodeLayer.setPickOnBounds(false);
        mConnectionLayer.setPickOnBounds(false);

        mNodeLayer.getStyleClass().add(STYLE_CLASS_NODE_LAYER);
        mConnectionLayer.getStyleClass().add(STYLE_CLASS_CONNECTION_LAYER);

        // Node layer should be on top of connection layer, so we add it second.
        getChildren().addAll(mGrid, mConnectionLayer, mNodeLayer, mSelectionBox);
    }
} public class DefaultConnectionLayouter implements ConnectionLayouter
{

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultConnectionLayouter.class);

    private final Set<GConnection> mDirty = new HashSet<>();
    private boolean mRedrawAll = false;
    private final Map<GConnectionSkin, Point2D[]> mConnectionPoints = new HashMap<>();
    private final SkinLookup mSkinLookup;
    private GModel mModel;

    /**
     * Creates a new {@link DefaultConnectionLayouter} instance. Only one
     * instance should exist per {@link DefaultGraphEditor} instance.
     *
     * @param pSkinLookup
     *            the {@link SkinLookup} used to look up skins
     */
    public DefaultConnectionLayouter(final SkinLookup pSkinLookup)
    {
        mSkinLookup = pSkinLookup;
    }

    @Override
    public void initialize(final GModel pModel)
    {
        mModel = pModel;
    }

    @Override
    public void redraw(final Collection<GConnection> pConnections)
    {
        mDirty.addAll(pConnections);
    }

    @Override
    public void redraw(final GConnection pConnection)
    {
        mDirty.add(pConnection);
    }

    @Override
    public void redrawAll()
    {
        mRedrawAll = true;
    }

    @Override
    public void draw()
    {
        if (mModel == null || mModel.getConnections().isEmpty())
        {
            return;
        }

        try
        {
            if (mRedrawAll)
            {
                mConnectionPoints.clear();
                if (!mModel.getConnections().isEmpty())
                {
                    redrawAllConnections();
                }
                mRedrawAll = false;
            }
            else if (!mDirty.isEmpty())
            {
                final List<GConnectionSkin> repaint = new ArrayList<>(mDirty.size());
                for (final GConnection conn : mDirty)
                {
                    final GConnectionSkin connectionSkin = mSkinLookup.lookupConnection(conn);
                    if (connectionSkin != null && !(connectionSkin instanceof VirtualSkin))
                    {
                        final Point2D[] points = connectionSkin.update();
                        if (points != null)
                        {
                            mConnectionPoints.put(connectionSkin, points);
                        }

                        repaint.add(connectionSkin);
                    }
                }

                for (final GConnectionSkin skin : repaint)
                {
                    skin.draw(mConnectionPoints);
                }
                mDirty.clear();
            }

        }
        catch (Exception e)
        {
            LOGGER.debug("Could not redraw Connections: ", e); //$NON-NLS-1$
        }
    }

    private void redrawAllConnections()
    {
        for (final GConnection connection : mModel.getConnections())
        {
            final GConnectionSkin connectionSkin = mSkinLookup.lookupConnection(connection);
            if (connectionSkin != null)
            {
                final Point2D[] points = connectionSkin.update();
                if (points != null)
                {
                    mConnectionPoints.put(connectionSkin, points);
                }
            }
        }

        for (final GConnectionSkin skin : mConnectionPoints.keySet())
        {
            skin.draw(mConnectionPoints);
        }
    }
} public interface GConnection extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Source</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Source</em>' reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Source</em>' reference.
	 * @see #setSource(GConnector)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Source()
	 * @model required="true"
	 * @generated
	 */
	GConnector getSource();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getSource <em>Source</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Source</em>' reference.
	 * @see #getSource()
	 * @generated
	 */
	void setSource(GConnector value);

	/**
	 * Returns the value of the '<em><b>Target</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Target</em>' reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Target</em>' reference.
	 * @see #setTarget(GConnector)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Target()
	 * @model required="true"
	 * @generated
	 */
	GConnector getTarget();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnection#getTarget <em>Target</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Target</em>' reference.
	 * @see #getTarget()
	 * @generated
	 */
	void setTarget(GConnector value);

	/**
	 * Returns the value of the '<em><b>Joints</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GJoint}.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GJoint#getConnection <em>Connection</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Joints</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Joints</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnection_Joints()
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getConnection
	 * @model opposite="connection" containment="true"
	 * @generated
	 */
	EList<GJoint> getJoints();

} // GConnection public interface GConnector extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Parent</b></em>' container reference.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GNode#getConnectors <em>Connectors</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Parent</em>' container reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Parent</em>' container reference.
	 * @see #setParent(GNode)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Parent()
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getConnectors
	 * @model opposite="connectors" required="true" transient="false"
	 * @generated
	 */
	GNode getParent();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getParent <em>Parent</em>}' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Parent</em>' container reference.
	 * @see #getParent()
	 * @generated
	 */
	void setParent(GNode value);

	/**
	 * Returns the value of the '<em><b>Connections</b></em>' reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnection}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connections</em>' reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connections</em>' reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Connections()
	 * @model
	 * @generated
	 */
	EList<GConnection> getConnections();

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_X()
	 * @model
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_Y()
	 * @model
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

	/**
	 * Returns the value of the '<em><b>Connection Detached On Drag</b></em>' attribute.
	 * The default value is <code>"true"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connection Detached On Drag</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connection Detached On Drag</em>' attribute.
	 * @see #setConnectionDetachedOnDrag(boolean)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGConnector_ConnectionDetachedOnDrag()
	 * @model default="true" required="true"
	 * @generated
	 */
	boolean isConnectionDetachedOnDrag();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GConnector#isConnectionDetachedOnDrag <em>Connection Detached On Drag</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Connection Detached On Drag</em>' attribute.
	 * @see #isConnectionDetachedOnDrag()
	 * @generated
	 */
	void setConnectionDetachedOnDrag(boolean value);

} // GConnector public interface GJoint extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Connection</b></em>' container reference.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GConnection#getJoints <em>Joints</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connection</em>' container reference isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connection</em>' container reference.
	 * @see #setConnection(GConnection)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Connection()
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getJoints
	 * @model opposite="joints" required="true" transient="false"
	 * @generated
	 */
	GConnection getConnection();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getConnection <em>Connection</em>}' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Connection</em>' container reference.
	 * @see #getConnection()
	 * @generated
	 */
	void setConnection(GConnection value);

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_X()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGJoint_Y()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GJoint#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

} // GJoint public interface GModel extends EObject {
	/**
	 * Returns the value of the '<em><b>Nodes</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GNode}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Nodes</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Nodes</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Nodes()
	 * @model containment="true"
	 * @generated
	 */
	EList<GNode> getNodes();

	/**
	 * Returns the value of the '<em><b>Connections</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnection}.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connections</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connections</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Connections()
	 * @model containment="true"
	 * @generated
	 */
	EList<GConnection> getConnections();

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>Content Width</b></em>' attribute.
	 * The default value is <code>"3000"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Content Width</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Content Width</em>' attribute.
	 * @see #setContentWidth(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_ContentWidth()
	 * @model default="3000" required="true"
	 * @generated
	 */
	double getContentWidth();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentWidth <em>Content Width</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Content Width</em>' attribute.
	 * @see #getContentWidth()
	 * @generated
	 */
	void setContentWidth(double value);

	/**
	 * Returns the value of the '<em><b>Content Height</b></em>' attribute.
	 * The default value is <code>"2250"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Content Height</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Content Height</em>' attribute.
	 * @see #setContentHeight(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGModel_ContentHeight()
	 * @model default="2250" required="true"
	 * @generated
	 */
	double getContentHeight();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentHeight <em>Content Height</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Content Height</em>' attribute.
	 * @see #getContentHeight()
	 * @generated
	 */
	void setContentHeight(double value);

} // GModel public interface GNode extends EObject {
	/**
	 * Returns the value of the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Id</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Id</em>' attribute.
	 * @see #setId(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Id()
	 * @model id="true"
	 * @generated
	 */
	String getId();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getId <em>Id</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Id</em>' attribute.
	 * @see #getId()
	 * @generated
	 */
	void setId(String value);

	/**
	 * Returns the value of the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Type</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Type</em>' attribute.
	 * @see #setType(String)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Type()
	 * @model
	 * @generated
	 */
	String getType();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getType <em>Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Type</em>' attribute.
	 * @see #getType()
	 * @generated
	 */
	void setType(String value);

	/**
	 * Returns the value of the '<em><b>X</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>X</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>X</em>' attribute.
	 * @see #setX(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_X()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getX();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getX <em>X</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>X</em>' attribute.
	 * @see #getX()
	 * @generated
	 */
	void setX(double value);

	/**
	 * Returns the value of the '<em><b>Y</b></em>' attribute.
	 * The default value is <code>"0"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Y</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Y</em>' attribute.
	 * @see #setY(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Y()
	 * @model default="0" required="true"
	 * @generated
	 */
	double getY();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getY <em>Y</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Y</em>' attribute.
	 * @see #getY()
	 * @generated
	 */
	void setY(double value);

	/**
	 * Returns the value of the '<em><b>Width</b></em>' attribute.
	 * The default value is <code>"151"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Width</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Width</em>' attribute.
	 * @see #setWidth(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Width()
	 * @model default="151" required="true"
	 * @generated
	 */
	double getWidth();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getWidth <em>Width</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Width</em>' attribute.
	 * @see #getWidth()
	 * @generated
	 */
	void setWidth(double value);

	/**
	 * Returns the value of the '<em><b>Height</b></em>' attribute.
	 * The default value is <code>"101"</code>.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Height</em>' attribute isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Height</em>' attribute.
	 * @see #setHeight(double)
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Height()
	 * @model default="101" required="true"
	 * @generated
	 */
	double getHeight();

	/**
	 * Sets the value of the '{@link de.tesis.dynaware.grapheditor.model.GNode#getHeight <em>Height</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Height</em>' attribute.
	 * @see #getHeight()
	 * @generated
	 */
	void setHeight(double value);

	/**
	 * Returns the value of the '<em><b>Connectors</b></em>' containment reference list.
	 * The list contents are of type {@link de.tesis.dynaware.grapheditor.model.GConnector}.
	 * It is bidirectional and its opposite is '{@link de.tesis.dynaware.grapheditor.model.GConnector#getParent <em>Parent</em>}'.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of the '<em>Connectors</em>' containment reference list isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Connectors</em>' containment reference list.
	 * @see de.tesis.dynaware.grapheditor.model.GraphPackage#getGNode_Connectors()
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getParent
	 * @model opposite="parent" containment="true"
	 * @generated
	 */
	EList<GConnector> getConnectors();

} // GNode public interface GraphPackage extends EPackage {
	/**
	 * The package name.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	String eNAME = "model";

	/**
	 * The package namespace URI.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	String eNS_URI = "http://de.tesis.dynaware.grapheditor.model/1.0";

	/**
	 * The package namespace name.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	String eNS_PREFIX = "graph";

	/**
	 * The singleton instance of the package.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	GraphPackage eINSTANCE = de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl.init();

	/**
	 * The meta object id for the '{@link de.tesis.dynaware.grapheditor.model.impl.GModelImpl <em>GModel</em>}' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see de.tesis.dynaware.grapheditor.model.impl.GModelImpl
	 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGModel()
	 * @generated
	 */
	int GMODEL = 0;

	/**
	 * The feature id for the '<em><b>Nodes</b></em>' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL__NODES = 0;

	/**
	 * The feature id for the '<em><b>Connections</b></em>' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL__CONNECTIONS = 1;

	/**
	 * The feature id for the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL__TYPE = 2;

	/**
	 * The feature id for the '<em><b>Content Width</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL__CONTENT_WIDTH = 3;

	/**
	 * The feature id for the '<em><b>Content Height</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL__CONTENT_HEIGHT = 4;

	/**
	 * The number of structural features of the '<em>GModel</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL_FEATURE_COUNT = 5;

	/**
	 * The number of operations of the '<em>GModel</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GMODEL_OPERATION_COUNT = 0;

	/**
	 * The meta object id for the '{@link de.tesis.dynaware.grapheditor.model.impl.GNodeImpl <em>GNode</em>}' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see de.tesis.dynaware.grapheditor.model.impl.GNodeImpl
	 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGNode()
	 * @generated
	 */
	int GNODE = 1;

	/**
	 * The feature id for the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__ID = 0;

	/**
	 * The feature id for the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__TYPE = 1;

	/**
	 * The feature id for the '<em><b>X</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__X = 2;

	/**
	 * The feature id for the '<em><b>Y</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__Y = 3;

	/**
	 * The feature id for the '<em><b>Width</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__WIDTH = 4;

	/**
	 * The feature id for the '<em><b>Height</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__HEIGHT = 5;

	/**
	 * The feature id for the '<em><b>Connectors</b></em>' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE__CONNECTORS = 6;

	/**
	 * The number of structural features of the '<em>GNode</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE_FEATURE_COUNT = 7;

	/**
	 * The number of operations of the '<em>GNode</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GNODE_OPERATION_COUNT = 0;

	/**
	 * The meta object id for the '{@link de.tesis.dynaware.grapheditor.model.impl.GConnectionImpl <em>GConnection</em>}' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see de.tesis.dynaware.grapheditor.model.impl.GConnectionImpl
	 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGConnection()
	 * @generated
	 */
	int GCONNECTION = 2;

	/**
	 * The feature id for the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION__ID = 0;

	/**
	 * The feature id for the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION__TYPE = 1;

	/**
	 * The feature id for the '<em><b>Source</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION__SOURCE = 2;

	/**
	 * The feature id for the '<em><b>Target</b></em>' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION__TARGET = 3;

	/**
	 * The feature id for the '<em><b>Joints</b></em>' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION__JOINTS = 4;

	/**
	 * The number of structural features of the '<em>GConnection</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION_FEATURE_COUNT = 5;

	/**
	 * The number of operations of the '<em>GConnection</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTION_OPERATION_COUNT = 0;

	/**
	 * The meta object id for the '{@link de.tesis.dynaware.grapheditor.model.impl.GConnectorImpl <em>GConnector</em>}' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see de.tesis.dynaware.grapheditor.model.impl.GConnectorImpl
	 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGConnector()
	 * @generated
	 */
	int GCONNECTOR = 3;

	/**
	 * The feature id for the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__ID = 0;

	/**
	 * The feature id for the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__TYPE = 1;

	/**
	 * The feature id for the '<em><b>Parent</b></em>' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__PARENT = 2;

	/**
	 * The feature id for the '<em><b>Connections</b></em>' reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__CONNECTIONS = 3;

	/**
	 * The feature id for the '<em><b>X</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__X = 4;

	/**
	 * The feature id for the '<em><b>Y</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__Y = 5;

	/**
	 * The feature id for the '<em><b>Connection Detached On Drag</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR__CONNECTION_DETACHED_ON_DRAG = 6;

	/**
	 * The number of structural features of the '<em>GConnector</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR_FEATURE_COUNT = 7;

	/**
	 * The number of operations of the '<em>GConnector</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GCONNECTOR_OPERATION_COUNT = 0;

	/**
	 * The meta object id for the '{@link de.tesis.dynaware.grapheditor.model.impl.GJointImpl <em>GJoint</em>}' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see de.tesis.dynaware.grapheditor.model.impl.GJointImpl
	 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGJoint()
	 * @generated
	 */
	int GJOINT = 4;

	/**
	 * The feature id for the '<em><b>Id</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT__ID = 0;

	/**
	 * The feature id for the '<em><b>Type</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT__TYPE = 1;

	/**
	 * The feature id for the '<em><b>Connection</b></em>' container reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT__CONNECTION = 2;

	/**
	 * The feature id for the '<em><b>X</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT__X = 3;

	/**
	 * The feature id for the '<em><b>Y</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT__Y = 4;

	/**
	 * The number of structural features of the '<em>GJoint</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT_FEATURE_COUNT = 5;

	/**
	 * The number of operations of the '<em>GJoint</em>' class.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 * @ordered
	 */
	int GJOINT_OPERATION_COUNT = 0;


	/**
	 * Returns the meta object for class '{@link de.tesis.dynaware.grapheditor.model.GModel <em>GModel</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for class '<em>GModel</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel
	 * @generated
	 */
	EClass getGModel();

	/**
	 * Returns the meta object for the containment reference list '{@link de.tesis.dynaware.grapheditor.model.GModel#getNodes <em>Nodes</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the containment reference list '<em>Nodes</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel#getNodes()
	 * @see #getGModel()
	 * @generated
	 */
	EReference getGModel_Nodes();

	/**
	 * Returns the meta object for the containment reference list '{@link de.tesis.dynaware.grapheditor.model.GModel#getConnections <em>Connections</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the containment reference list '<em>Connections</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel#getConnections()
	 * @see #getGModel()
	 * @generated
	 */
	EReference getGModel_Connections();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GModel#getType <em>Type</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Type</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel#getType()
	 * @see #getGModel()
	 * @generated
	 */
	EAttribute getGModel_Type();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentWidth <em>Content Width</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Content Width</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel#getContentWidth()
	 * @see #getGModel()
	 * @generated
	 */
	EAttribute getGModel_ContentWidth();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GModel#getContentHeight <em>Content Height</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Content Height</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GModel#getContentHeight()
	 * @see #getGModel()
	 * @generated
	 */
	EAttribute getGModel_ContentHeight();

	/**
	 * Returns the meta object for class '{@link de.tesis.dynaware.grapheditor.model.GNode <em>GNode</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for class '<em>GNode</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode
	 * @generated
	 */
	EClass getGNode();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getId <em>Id</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Id</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getId()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_Id();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getType <em>Type</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Type</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getType()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_Type();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getX <em>X</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>X</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getX()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_X();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getY <em>Y</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Y</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getY()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_Y();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getWidth <em>Width</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Width</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getWidth()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_Width();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GNode#getHeight <em>Height</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Height</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getHeight()
	 * @see #getGNode()
	 * @generated
	 */
	EAttribute getGNode_Height();

	/**
	 * Returns the meta object for the containment reference list '{@link de.tesis.dynaware.grapheditor.model.GNode#getConnectors <em>Connectors</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the containment reference list '<em>Connectors</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GNode#getConnectors()
	 * @see #getGNode()
	 * @generated
	 */
	EReference getGNode_Connectors();

	/**
	 * Returns the meta object for class '{@link de.tesis.dynaware.grapheditor.model.GConnection <em>GConnection</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for class '<em>GConnection</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection
	 * @generated
	 */
	EClass getGConnection();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnection#getId <em>Id</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Id</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getId()
	 * @see #getGConnection()
	 * @generated
	 */
	EAttribute getGConnection_Id();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnection#getType <em>Type</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Type</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getType()
	 * @see #getGConnection()
	 * @generated
	 */
	EAttribute getGConnection_Type();

	/**
	 * Returns the meta object for the reference '{@link de.tesis.dynaware.grapheditor.model.GConnection#getSource <em>Source</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the reference '<em>Source</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getSource()
	 * @see #getGConnection()
	 * @generated
	 */
	EReference getGConnection_Source();

	/**
	 * Returns the meta object for the reference '{@link de.tesis.dynaware.grapheditor.model.GConnection#getTarget <em>Target</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the reference '<em>Target</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getTarget()
	 * @see #getGConnection()
	 * @generated
	 */
	EReference getGConnection_Target();

	/**
	 * Returns the meta object for the containment reference list '{@link de.tesis.dynaware.grapheditor.model.GConnection#getJoints <em>Joints</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the containment reference list '<em>Joints</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnection#getJoints()
	 * @see #getGConnection()
	 * @generated
	 */
	EReference getGConnection_Joints();

	/**
	 * Returns the meta object for class '{@link de.tesis.dynaware.grapheditor.model.GConnector <em>GConnector</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for class '<em>GConnector</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector
	 * @generated
	 */
	EClass getGConnector();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnector#getId <em>Id</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Id</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getId()
	 * @see #getGConnector()
	 * @generated
	 */
	EAttribute getGConnector_Id();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnector#getType <em>Type</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Type</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getType()
	 * @see #getGConnector()
	 * @generated
	 */
	EAttribute getGConnector_Type();

	/**
	 * Returns the meta object for the container reference '{@link de.tesis.dynaware.grapheditor.model.GConnector#getParent <em>Parent</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the container reference '<em>Parent</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getParent()
	 * @see #getGConnector()
	 * @generated
	 */
	EReference getGConnector_Parent();

	/**
	 * Returns the meta object for the reference list '{@link de.tesis.dynaware.grapheditor.model.GConnector#getConnections <em>Connections</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the reference list '<em>Connections</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getConnections()
	 * @see #getGConnector()
	 * @generated
	 */
	EReference getGConnector_Connections();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnector#getX <em>X</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>X</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getX()
	 * @see #getGConnector()
	 * @generated
	 */
	EAttribute getGConnector_X();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnector#getY <em>Y</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Y</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#getY()
	 * @see #getGConnector()
	 * @generated
	 */
	EAttribute getGConnector_Y();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GConnector#isConnectionDetachedOnDrag <em>Connection Detached On Drag</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Connection Detached On Drag</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GConnector#isConnectionDetachedOnDrag()
	 * @see #getGConnector()
	 * @generated
	 */
	EAttribute getGConnector_ConnectionDetachedOnDrag();

	/**
	 * Returns the meta object for class '{@link de.tesis.dynaware.grapheditor.model.GJoint <em>GJoint</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for class '<em>GJoint</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint
	 * @generated
	 */
	EClass getGJoint();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GJoint#getId <em>Id</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Id</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getId()
	 * @see #getGJoint()
	 * @generated
	 */
	EAttribute getGJoint_Id();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GJoint#getType <em>Type</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Type</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getType()
	 * @see #getGJoint()
	 * @generated
	 */
	EAttribute getGJoint_Type();

	/**
	 * Returns the meta object for the container reference '{@link de.tesis.dynaware.grapheditor.model.GJoint#getConnection <em>Connection</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the container reference '<em>Connection</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getConnection()
	 * @see #getGJoint()
	 * @generated
	 */
	EReference getGJoint_Connection();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GJoint#getX <em>X</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>X</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getX()
	 * @see #getGJoint()
	 * @generated
	 */
	EAttribute getGJoint_X();

	/**
	 * Returns the meta object for the attribute '{@link de.tesis.dynaware.grapheditor.model.GJoint#getY <em>Y</em>}'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the meta object for the attribute '<em>Y</em>'.
	 * @see de.tesis.dynaware.grapheditor.model.GJoint#getY()
	 * @see #getGJoint()
	 * @generated
	 */
	EAttribute getGJoint_Y();

	/**
	 * Returns the factory that creates the instances of the model.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the factory that creates the instances of the model.
	 * @generated
	 */
	GraphFactory getGraphFactory();

	/**
	 * <!-- begin-user-doc -->
	 * Defines literals for the meta objects that represent
	 * <ul>
	 *   <li>each class,</li>
	 *   <li>each feature of each class,</li>
	 *   <li>each operation of each class,</li>
	 *   <li>each enum,</li>
	 *   <li>and each data type</li>
	 * </ul>
	 * <!-- end-user-doc -->
	 * @generated
	 */
	interface Literals {
		/**
		 * The meta object literal for the '{@link de.tesis.dynaware.grapheditor.model.impl.GModelImpl <em>GModel</em>}' class.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @see de.tesis.dynaware.grapheditor.model.impl.GModelImpl
		 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGModel()
		 * @generated
		 */
		EClass GMODEL = eINSTANCE.getGModel();

		/**
		 * The meta object literal for the '<em><b>Nodes</b></em>' containment reference list feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GMODEL__NODES = eINSTANCE.getGModel_Nodes();

		/**
		 * The meta object literal for the '<em><b>Connections</b></em>' containment reference list feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GMODEL__CONNECTIONS = eINSTANCE.getGModel_Connections();

		/**
		 * The meta object literal for the '<em><b>Type</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GMODEL__TYPE = eINSTANCE.getGModel_Type();

		/**
		 * The meta object literal for the '<em><b>Content Width</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GMODEL__CONTENT_WIDTH = eINSTANCE.getGModel_ContentWidth();

		/**
		 * The meta object literal for the '<em><b>Content Height</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GMODEL__CONTENT_HEIGHT = eINSTANCE.getGModel_ContentHeight();

		/**
		 * The meta object literal for the '{@link de.tesis.dynaware.grapheditor.model.impl.GNodeImpl <em>GNode</em>}' class.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @see de.tesis.dynaware.grapheditor.model.impl.GNodeImpl
		 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGNode()
		 * @generated
		 */
		EClass GNODE = eINSTANCE.getGNode();

		/**
		 * The meta object literal for the '<em><b>Id</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__ID = eINSTANCE.getGNode_Id();

		/**
		 * The meta object literal for the '<em><b>Type</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__TYPE = eINSTANCE.getGNode_Type();

		/**
		 * The meta object literal for the '<em><b>X</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__X = eINSTANCE.getGNode_X();

		/**
		 * The meta object literal for the '<em><b>Y</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__Y = eINSTANCE.getGNode_Y();

		/**
		 * The meta object literal for the '<em><b>Width</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__WIDTH = eINSTANCE.getGNode_Width();

		/**
		 * The meta object literal for the '<em><b>Height</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GNODE__HEIGHT = eINSTANCE.getGNode_Height();

		/**
		 * The meta object literal for the '<em><b>Connectors</b></em>' containment reference list feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GNODE__CONNECTORS = eINSTANCE.getGNode_Connectors();

		/**
		 * The meta object literal for the '{@link de.tesis.dynaware.grapheditor.model.impl.GConnectionImpl <em>GConnection</em>}' class.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @see de.tesis.dynaware.grapheditor.model.impl.GConnectionImpl
		 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGConnection()
		 * @generated
		 */
		EClass GCONNECTION = eINSTANCE.getGConnection();

		/**
		 * The meta object literal for the '<em><b>Id</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTION__ID = eINSTANCE.getGConnection_Id();

		/**
		 * The meta object literal for the '<em><b>Type</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTION__TYPE = eINSTANCE.getGConnection_Type();

		/**
		 * The meta object literal for the '<em><b>Source</b></em>' reference feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GCONNECTION__SOURCE = eINSTANCE.getGConnection_Source();

		/**
		 * The meta object literal for the '<em><b>Target</b></em>' reference feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GCONNECTION__TARGET = eINSTANCE.getGConnection_Target();

		/**
		 * The meta object literal for the '<em><b>Joints</b></em>' containment reference list feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GCONNECTION__JOINTS = eINSTANCE.getGConnection_Joints();

		/**
		 * The meta object literal for the '{@link de.tesis.dynaware.grapheditor.model.impl.GConnectorImpl <em>GConnector</em>}' class.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @see de.tesis.dynaware.grapheditor.model.impl.GConnectorImpl
		 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGConnector()
		 * @generated
		 */
		EClass GCONNECTOR = eINSTANCE.getGConnector();

		/**
		 * The meta object literal for the '<em><b>Id</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTOR__ID = eINSTANCE.getGConnector_Id();

		/**
		 * The meta object literal for the '<em><b>Type</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTOR__TYPE = eINSTANCE.getGConnector_Type();

		/**
		 * The meta object literal for the '<em><b>Parent</b></em>' container reference feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GCONNECTOR__PARENT = eINSTANCE.getGConnector_Parent();

		/**
		 * The meta object literal for the '<em><b>Connections</b></em>' reference list feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GCONNECTOR__CONNECTIONS = eINSTANCE.getGConnector_Connections();

		/**
		 * The meta object literal for the '<em><b>X</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTOR__X = eINSTANCE.getGConnector_X();

		/**
		 * The meta object literal for the '<em><b>Y</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTOR__Y = eINSTANCE.getGConnector_Y();

		/**
		 * The meta object literal for the '<em><b>Connection Detached On Drag</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GCONNECTOR__CONNECTION_DETACHED_ON_DRAG = eINSTANCE.getGConnector_ConnectionDetachedOnDrag();

		/**
		 * The meta object literal for the '{@link de.tesis.dynaware.grapheditor.model.impl.GJointImpl <em>GJoint</em>}' class.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @see de.tesis.dynaware.grapheditor.model.impl.GJointImpl
		 * @see de.tesis.dynaware.grapheditor.model.impl.GraphPackageImpl#getGJoint()
		 * @generated
		 */
		EClass GJOINT = eINSTANCE.getGJoint();

		/**
		 * The meta object literal for the '<em><b>Id</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GJOINT__ID = eINSTANCE.getGJoint_Id();

		/**
		 * The meta object literal for the '<em><b>Type</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GJOINT__TYPE = eINSTANCE.getGJoint_Type();

		/**
		 * The meta object literal for the '<em><b>Connection</b></em>' container reference feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EReference GJOINT__CONNECTION = eINSTANCE.getGJoint_Connection();

		/**
		 * The meta object literal for the '<em><b>X</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GJOINT__X = eINSTANCE.getGJoint_X();

		/**
		 * The meta object literal for the '<em><b>Y</b></em>' attribute feature.
		 * <!-- begin-user-doc -->
		 * <!-- end-user-doc -->
		 * @generated
		 */
		EAttribute GJOINT__Y = eINSTANCE.getGJoint_Y();

	}

} //GraphPackage public class GraphEditorProperties implements GraphEventManager
{

    /**
     * The default max width of the editor region, set on startup.
     */
    public static final double DEFAULT_MAX_WIDTH = Double.MAX_VALUE;
    /**
     * The default max height of the editor region, set on startup.
     */
    public static final double DEFAULT_MAX_HEIGHT = Double.MAX_VALUE;

    public static final double DEFAULT_BOUND_VALUE = 15;
    public static final double DEFAULT_GRID_SPACING = 12;

    // The distance from the editor edge at which the objects should stop when dragged / resized.
    private double northBoundValue = DEFAULT_BOUND_VALUE;
    private double southBoundValue = DEFAULT_BOUND_VALUE;
    private double eastBoundValue = DEFAULT_BOUND_VALUE;
    private double westBoundValue = DEFAULT_BOUND_VALUE;

    // Off by default.
    private final BooleanProperty gridVisible = new SimpleBooleanProperty(this, "gridVisible"); //$NON-NLS-1$
    private final BooleanProperty snapToGrid = new SimpleBooleanProperty(this, "snapToGrid"); //$NON-NLS-1$
    private final DoubleProperty gridSpacing = new SimpleDoubleProperty(this, "gridSpacing", DEFAULT_GRID_SPACING); //$NON-NLS-1$

    private final Map<EditorElement, BooleanProperty> readOnly = new EnumMap<>(EditorElement.class);

    private final ObservableMap<String, String> customProperties = FXCollections.observableHashMap();

    private final GraphEventManager eventManager = new GraphEventManagerImpl();

    /**
     * Creates a new editor properties instance containing a set of default
     * properties.
     */
    public GraphEditorProperties()
    {
    }

    /**
     * Copy constructor.
     *
     * <p>
     * Creates a new editor properties instance with all values copied over from
     * an existing instance.
     * </p>
     *
     * @param editorProperties
     *         an existing {@link GraphEditorProperties} instance
     */
    public GraphEditorProperties(final GraphEditorProperties editorProperties)
    {
        northBoundValue = editorProperties.getNorthBoundValue();
        southBoundValue = editorProperties.getSouthBoundValue();
        eastBoundValue = editorProperties.getEastBoundValue();
        westBoundValue = editorProperties.getWestBoundValue();

        gridVisible.set(editorProperties.isGridVisible());
        snapToGrid.set(editorProperties.isSnapToGridOn());
        gridSpacing.set(editorProperties.getGridSpacing());

        for (final Map.Entry<EditorElement, BooleanProperty> entry : editorProperties.readOnly.entrySet())
        {
            readOnly.computeIfAbsent(entry.getKey(), k -> new SimpleBooleanProperty()).set(entry.getValue().get());
        }

        customProperties.putAll(editorProperties.getCustomProperties());
    }

    /**
     * Gets the value of the north bound.
     *
     * @return the value of the north bound
     */
    public double getNorthBoundValue()
    {
        return northBoundValue;
    }

    /**
     * Sets the value of the north bound.
     *
     * @param pNorthBoundValue
     *         the value of the north bound
     */
    public void setNorthBoundValue(final double pNorthBoundValue)
    {
        northBoundValue = pNorthBoundValue;
    }

    /**
     * Gets the value of the south bound.
     *
     * @return the value of the south bound
     */
    public double getSouthBoundValue()
    {
        return southBoundValue;
    }

    /**
     * Sets the value of the south bound.
     *
     * @param pSouthBoundValue
     *         the value of the south bound
     */
    public void setSouthBoundValue(final double pSouthBoundValue)
    {
        southBoundValue = pSouthBoundValue;
    }

    /**
     * Gets the value of the east bound.
     *
     * @return the value of the east bound
     */
    public double getEastBoundValue()
    {
        return eastBoundValue;
    }

    /**
     * Sets the value of the east bound.
     *
     * @param pEastBoundValue
     *         the value of the east bound
     */
    public void setEastBoundValue(final double pEastBoundValue)
    {
        eastBoundValue = pEastBoundValue;
    }

    /**
     * Gets the value of the west bound.
     *
     * @return the value of the west bound
     */
    public double getWestBoundValue()
    {
        return westBoundValue;
    }

    /**
     * Sets the value of the west bound.
     *
     * @param pWestBoundValue
     *         the value of the west bound
     */
    public void setWestBoundValue(final double pWestBoundValue)
    {
        westBoundValue = pWestBoundValue;
    }

    /**
     * Checks if the background grid is visible.
     *
     * @return {@code true} if the background grid is visible, {@code false} if
     *         not
     */
    public boolean isGridVisible()
    {
        return gridVisible.get();
    }

    /**
     * Sets whether the background grid should be visible or not.
     *
     * @param pGridVisible
     *            {@code true} if the background grid should be visible,
     *            {@code false} if not
     */
    public void setGridVisible(final boolean pGridVisible)
    {
        gridVisible.set(pGridVisible);
    }

    /**
     * Gets the grid-visible property.
     *
     * @return a {@link BooleanProperty} tracking whether the grid is visible or
     *         not
     */
    public BooleanProperty gridVisibleProperty()
    {
        return gridVisible;
    }

    /**
     * Checks if snap-to-grid is on.
     *
     * @return {@code true} if snap-to-grid is on, {@code false} if not
     */
    public boolean isSnapToGridOn()
    {
        return snapToGrid.get();
    }

    /**
     * Sets whether snap-to-grid should be on.
     *
     * @param pSnapToGrid
     *            {@code true} if snap-to-grid should be on, {@code false} if
     *            not
     */
    public void setSnapToGrid(final boolean pSnapToGrid)
    {
        snapToGrid.set(pSnapToGrid);
    }

    /**
     * Gets the snap-to-grid property.
     *
     * @return a {@link BooleanProperty} tracking whether snap-to-grid is on or
     *         off
     */
    public BooleanProperty snapToGridProperty()
    {
        return snapToGrid;
    }

    /**
     * Gets the current grid spacing in pixels.
     *
     * @return the current grid spacing
     */
    public double getGridSpacing()
    {
        return gridSpacing.get();
    }

    /**
     * Sets the grid spacing to be used if the grid is visible and/or
     * snap-to-grid is enabled.
     *
     * <p>
     * Integer values are recommended to avoid sub-pixel positioning effects.
     * </p>
     *
     * @param pGridSpacing
     *         the grid spacing to be used
     */
    public void setGridSpacing(final double pGridSpacing)
    {
        gridSpacing.set(pGridSpacing);
    }

    /**
     * Gets the grid spacing property.
     *
     * @return the grid spacing {@link DoubleProperty}.
     */
    public DoubleProperty gridSpacingProperty()
    {
        return gridSpacing;
    }

    /**
     * Gets the read only property
     *
     * @param pType
     *         {@link EditorElement}
     * @return read only {@link BooleanProperty}
     */
    public BooleanProperty readOnlyProperty(final EditorElement pType)
    {
        Objects.requireNonNull(pType, "ElementType may not be null!");
        return readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty());
    }

    /**
     * Returns whether or not the graph is in read only state.
     *
     * @param pType
     *         {@link EditorElement}
     * @return whether or not the graph is in read only state.
     */
    public boolean isReadOnly(final EditorElement pType)
    {
        return pType != null && readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty()).get();
    }

    /**
     * @param pType
     *         {@link EditorElement}
     * @param pReadOnly
     *         {@code true} to set the graph editor in read only state or {@code false} (default) for edit state.
     */
    public void setReadOnly(final EditorElement pType, final boolean pReadOnly)
    {
        if (pType != null)
        {
            readOnly.computeIfAbsent(pType, k -> new SimpleBooleanProperty()).set(pReadOnly);
        }
    }

    /**
     * Additional properties that may be added and referred to in custom skin
     * implementations.
     *
     * @return a map of custom properties
     */
    public ObservableMap<String, String> getCustomProperties()
    {
        return customProperties;
    }

    @Override
    public boolean activateGesture(GraphInputGesture pGesture, Event pEvent, Object pOwner)
    {
        return eventManager.activateGesture(pGesture, pEvent, pOwner);
    }

    @Override
    public boolean finishGesture(GraphInputGesture pExpected, Object pOwner)
    {
        return eventManager.finishGesture(pExpected, pOwner);
    }
}
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import org.eclipse.emf.common.command.CommandStackListener;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.tesis.dynaware.grapheditor.Commands;
import de.tesis.dynaware.grapheditor.GConnectorValidator;
import de.tesis.dynaware.grapheditor.GJointSkin;
import de.tesis.dynaware.grapheditor.GNodeSkin;
import de.tesis.dynaware.grapheditor.GraphEditor;
import de.tesis.dynaware.grapheditor.SelectionManager;
import de.tesis.dynaware.grapheditor.SkinLookup;
import de.tesis.dynaware.grapheditor.core.connections.ConnectionEventManager;
import de.tesis.dynaware.grapheditor.core.connections.ConnectorDragManager;
import de.tesis.dynaware.grapheditor.core.model.DefaultModelEditingManager;
import de.tesis.dynaware.grapheditor.core.model.ModelLayoutUpdater;
import de.tesis.dynaware.grapheditor.core.model.ModelSanityChecker;
import de.tesis.dynaware.grapheditor.core.selections.DefaultSelectionManager;
import de.tesis.dynaware.grapheditor.core.skins.SkinManager;
import de.tesis.dynaware.grapheditor.core.view.ConnectionLayouter;
import de.tesis.dynaware.grapheditor.core.view.GraphEditorView;
import de.tesis.dynaware.grapheditor.core.view.impl.DefaultConnectionLayouter;
import de.tesis.dynaware.grapheditor.model.GConnection;
import de.tesis.dynaware.grapheditor.model.GConnector;
import de.tesis.dynaware.grapheditor.model.GJoint;
import de.tesis.dynaware.grapheditor.model.GModel;
import de.tesis.dynaware.grapheditor.model.GNode;
import de.tesis.dynaware.grapheditor.model.GraphPackage;
import de.tesis.dynaware.grapheditor.utils.GraphEditorProperties;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;

package de.tesis.dynaware.grapheditor.core;





/**
 * The central controller class for the default graph editor implementation.
 *
 * <p>
 * Responsible for using the {@link SkinManager} to create all skin instances
 * for the current {@link GModel}, and adding them to the {@link GraphEditorView
 * view}.
 * </p>
 *
 * <p>
 * Also responsible for creating all secondary managers like the
 * {@link ConnectorDragManager} and reinitializing them when the model changes.
 * </p>
 *
 * <p>
 * The process of synchronizing is rather complicated in case more than one
 * model is part of the resource set:
 * <ol>
 * <li>register listener on every model in the resource set (with an
 * {@link EContentAdapter}</li>
 * <li>receive notifications</li>
 * <li>put notification into queue</li>
 * <li>{@link #process() process queue} on every reload and/or command stack
 * change</li>
 * </ol>
 * This procedure (processing a chunk of notifications on command stack change
 * or {@link GraphEditor#reload()} is a very safe way to determine a valid
 * package of changes.
 * </p>
 *
 * <p>
 * This implementation is thread safe: It is able to process notifications in
 * parallel and processes them in chunks on the FX Thread.
 * </p>
 */
public class GraphEditorController<E extends GraphEditor>
{

    private static final Logger LOGGER = LoggerFactory.getLogger(GraphEditorController.class);

    private final GraphEditorEContentAdapter mContentAdapter = new GraphEditorEContentAdapter();

    private final Map<EStructuralFeature, Consumer<Notification>> mHandlersByFeature = new HashMap<>();
    private final Map<Integer, Consumer<Notification>> mHandlersByType = new HashMap<>();

    private final Collection<GNode> mNodeConnectorsDirty = new HashSet<>();
    private final Collection<GConnection> mConnectionsDirty = new HashSet<>();

    private final Collection<GConnection> mConnectionsToAdd = new HashSet<>();
    private final Collection<GNode> mNodesToAdd = new HashSet<>();
    private final Collection<GJoint> mJointsToAdd = new HashSet<>();
    private final Collection<GConnector> mConnectorsToAdd = new HashSet<>();

    private final CommandStackListener mCommandStackListener = event -> process();

    private final ModelEditingManager mModelEditingManager = new DefaultModelEditingManager(mCommandStackListener);
    private final ModelLayoutUpdater mModelLayoutUpdater;
    private final ConnectionLayouter mConnectionLayouter;
    private final ConnectorDragManager mConnectorDragManager;
    private final DefaultSelectionManager mSelectionManager;
    private final SkinManager mSkinManager;

    private final E mEditor;
    private final ChangeListener<GModel> mModelChangeListener = (w, o, n) -> modelChanged(o, n);

    /**
     * While processing a {@link Notification} from EMF it might trigger other
     * changes which will eventually lead to an infinite update cycle.<br>
     * When this flag is {@code true} we are currently processing a batch of
     * updates. All new notifications will be put in the queue but are processed
     * later on.
     */
    private boolean mProcessing = false;

    /**
     * Creates a new controller instance. Only one instance should exist per
     * {@link GraphEditor} instance.
     *
     * @param pEditor
     *            {@link GraphEditor} instance
     * @param pSkinManager
     *            the {@link SkinManager} instance
     * @param pView
     *            {@link GraphEditorView}
     * @param pConnectionEventManager
     *            the {@link ConnectionEventManager} instance
     */
    public GraphEditorController(final E pEditor, final SkinManager pSkinManager,
            final GraphEditorView pView, final ConnectionEventManager pConnectionEventManager, final GraphEditorProperties pProperties)
    {
        mEditor = Objects.requireNonNull(pEditor, "GraphEditor instance may not be null!");
        mConnectionLayouter = new DefaultConnectionLayouter(pSkinManager);

        mSkinManager = Objects.requireNonNull(pSkinManager, "SkinManager may not be null!");

        mModelLayoutUpdater = new ModelLayoutUpdater(pSkinManager, mModelEditingManager, pProperties);
        mConnectorDragManager = new ConnectorDragManager(pSkinManager, pConnectionEventManager, pView);
        mSelectionManager = new DefaultSelectionManager(pSkinManager, pView);

        initDefaultListeners();

        pEditor.modelProperty().addListener(new WeakChangeListener<>(mModelChangeListener));
        modelChanged(null, pEditor.getModel());
    }

    private void initDefaultListeners()
    {
        registerChangeListener(GraphPackage.Literals.GMODEL__NODES, e -> processNotification(e, this::addNode, this::removeNode));

        registerChangeListener(GraphPackage.Literals.GNODE__CONNECTORS,
                e -> processNotification(e, this::addConnector, this::removeConnector));

        registerChangeListener(GraphPackage.Literals.GNODE__CONNECTORS, e ->
        {
            if (e.getNotifier() instanceof GNode n)
            {
                // if the connector is removed, the parent element is null..
                // luckily getNotifier() still returns the GNode where the connector was removed from:
                markConnectorsDirty(n);
            }
        });
        registerChangeListener(GraphPackage.Literals.GMODEL__CONNECTIONS,
                e -> processNotification(e, this::addConnection, this::removeConnection));

        registerChangeListener(GraphPackage.Literals.GCONNECTION__JOINTS,
                e -> processNotification(e, (GJoint j) -> addJoint(j, e.getNotifier()),
                        (GJoint j) -> removeJoint(j, e.getNotifier())));

        registerChangeListener(GraphPackage.Literals.GJOINT__X, this::jointPositionChanged);
        registerChangeListener(GraphPackage.Literals.GJOINT__Y, this::jointPositionChanged);

        registerChangeListener(GraphPackage.Literals.GNODE__Y, this::nodePositionChanged);
        registerChangeListener(GraphPackage.Literals.GNODE__X, this::nodePositionChanged);

        registerChangeListener(GraphPackage.Literals.GNODE__HEIGHT, this::nodeSizeChanged);
        registerChangeListener(GraphPackage.Literals.GNODE__WIDTH, this::nodeSizeChanged);

        registerChangeListener(GraphPackage.Literals.GNODE__TYPE, e ->
        {
            final GNode node = (GNode) e.getNotifier();
            removeNode(node);
            addNode(node);
        });
    }

    /**
     * Registers a change listener
     *
     * @param pFeature
     *            {@link EStructuralFeature feature to watch}
     * @param pHandler
     *            {@link Consumer} handling the {@link Notification}
     * @since 15.03.2019
     */
    public final void registerChangeListener(final EStructuralFeature pFeature, final Consumer<Notification> pHandler)
    {
        Objects.requireNonNull(pFeature, "EStructuralFeature may not be null!");
        Objects.requireNonNull(pHandler, "Notification Consumer may not be null!");
        mHandlersByFeature.merge(pFeature, pHandler, Consumer::andThen);
    }

    /**
     * Registers a change listener
     *
     * @param pNotificationType
     *            {@link Notification notification type}
     * @param pHandler
     *            {@link Consumer} handling the {@link Notification}
     * @since 15.03.2019
     */
    public final void registerChangeListener(final int pNotificationType, final Consumer<Notification> pHandler)
    {
        Objects.requireNonNull(pHandler, "Notification Consumer may not be null!");
        mHandlersByType.merge(pNotificationType, pHandler, Consumer::andThen);
    }

    private void modelChanged(final GModel pOldModel, final GModel pNewModel)
    {
        if (pOldModel != null)
        {
            final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(pOldModel);
            editingDomain.getResourceSet().eAdapters().remove(mContentAdapter);

            for (int i = 0; i < pOldModel.getNodes().size(); i++)
            {
                removeNode(pOldModel.getNodes().get(i));
            }
            for (int i = 0; i < pOldModel.getConnections().size(); i++)
            {
                removeConnection(pOldModel.getConnections().get(i));
            }
        }

        // remove any remaining skins that might have been left over:
        mSkinManager.clear();

        if (pNewModel != null)
        {

            ModelSanityChecker.validate(pNewModel);
