/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kie.workbench.common.stunner.core.graph.command.impl;
import java.util.Collection;
import java.util.Optional;
import org.jboss.errai.common.client.api.annotations.MapsTo;
import org.jboss.errai.common.client.api.annotations.Portable;
import org.kie.workbench.common.stunner.core.command.CommandResult;
import org.kie.workbench.common.stunner.core.graph.Edge;
import org.kie.workbench.common.stunner.core.graph.Node;
import org.kie.workbench.common.stunner.core.graph.command.GraphCommandExecutionContext;
import org.kie.workbench.common.stunner.core.graph.command.GraphCommandResultBuilder;
import org.kie.workbench.common.stunner.core.graph.content.view.View;
import org.kie.workbench.common.stunner.core.graph.content.view.ViewConnector;
import org.kie.workbench.common.stunner.core.rule.RuleViolation;
import org.kie.workbench.common.stunner.core.rule.context.CardinalityContext;
import org.kie.workbench.common.stunner.core.rule.context.EdgeCardinalityContext;
import org.kie.workbench.common.stunner.core.rule.context.impl.RuleContextBuilder;
import org.uberfire.commons.validation.PortablePreconditions;
/**
* A Command to set the outgoing connection for an edge.
* Notes:
* - In case <code>sourceNode</code> is <code>null</code>, connector's source node, if any, will be removed.
* - if connector is not view based, no need to provide magnet index.
*/
@Portable
public final class SetConnectionSourceNodeCommand extends AbstractGraphCommand {
private final String sourceNodeUUID;
private final String edgeUUID;
private final Integer magnetIndex;
private String lastSourceNodeUUID;
private Integer lastMagnetIndex;
private transient Edge<? extends View, Node> edge;
private transient Node<? extends View<?>, Edge> targetNode;
private transient Node<? extends View<?>, Edge> sourceNode;
@SuppressWarnings("unchecked")
public SetConnectionSourceNodeCommand(final @MapsTo("sourceNodeUUID") String sourceNodeUUID,
final @MapsTo("edgeUUID") String edgeUUID,
final @MapsTo("magnetIndex") Integer magnetIndex) {
this.edgeUUID = PortablePreconditions.checkNotNull("edgeUUID",
edgeUUID);
this.sourceNodeUUID = sourceNodeUUID;
this.magnetIndex = magnetIndex;
this.lastSourceNodeUUID = null;
this.lastMagnetIndex = null;
}
@SuppressWarnings("unchecked")
public SetConnectionSourceNodeCommand(final Node<? extends View<?>, Edge> sourceNode,
final Edge<? extends View, Node> edge,
final Integer magnetIndex) {
this(null != sourceNode ? sourceNode.getUUID() : null,
edge.getUUID(),
magnetIndex);
this.sourceNode = sourceNode;
this.edge = edge;
this.targetNode = edge.getTargetNode();
}
@SuppressWarnings("unchecked")
public SetConnectionSourceNodeCommand(final Node<? extends View<?>, Edge> sourceNode,
final Edge<? extends View, Node> edge) {
this(sourceNode,
edge,
null);
}
@Override
@SuppressWarnings("unchecked")
public CommandResult<RuleViolation> execute(final GraphCommandExecutionContext context) {
final CommandResult<RuleViolation> results = allow(context);
if (!results.getType().equals(CommandResult.Type.ERROR)) {
final Node<?, Edge> sourceNode = getSourceNode(context);
final Edge<? extends View, Node> edge = getEdge(context);
final Node<? extends View<?>, Edge> lastSourceNode = edge.getSourceNode();
if (null != lastSourceNode) {
this.lastSourceNodeUUID = lastSourceNode.getUUID();
lastSourceNode.getOutEdges().remove(edge);
}
if (null != sourceNode) {
sourceNode.getOutEdges().add(edge);
}
edge.setSourceNode(sourceNode);
if (null != magnetIndex) {
ViewConnector connectionContent = (ViewConnector) edge.getContent();
lastMagnetIndex = connectionContent.getSourceMagnetIndex();
connectionContent.setSourceMagnetIndex(magnetIndex);
}
}
return results;
}
@SuppressWarnings("unchecked")
protected CommandResult<RuleViolation> check(final GraphCommandExecutionContext context) {
final Node<View<?>, Edge> sourceNode = (Node<View<?>, Edge>) getSourceNode(context);
final Edge<View<?>, Node> edge = (Edge<View<?>, Node>) getEdge(context);
final GraphCommandResultBuilder resultBuilder = new GraphCommandResultBuilder();
final Collection<RuleViolation> connectionRuleViolations =
doEvaluate(context,
RuleContextBuilder.GraphContexts.connection(getGraph(context),
edge,
Optional.ofNullable(sourceNode),
Optional.ofNullable(targetNode)));
resultBuilder.addViolations(connectionRuleViolations);
final Node<View<?>, Edge> currentSource = edge.getSourceNode();
// If the edge has an outoutgoing source node, check cardinality for removing it.
if (null != currentSource) {
final Collection<RuleViolation> cardinalityRuleViolations =
doEvaluate(context,
RuleContextBuilder.GraphContexts.edgeCardinality(getGraph(context),
currentSource,
edge,
EdgeCardinalityContext.Direction.OUTGOING,
Optional.of(CardinalityContext.Operation.DELETE)));
resultBuilder.addViolations(cardinalityRuleViolations);
}
// If the new source node exist, evaluate cardinality rules for this edge.
if (null != sourceNode) {
final Collection<RuleViolation> cardinalityRuleViolations =
doEvaluate(context,
RuleContextBuilder.GraphContexts.edgeCardinality(getGraph(context),
sourceNode,
edge,
EdgeCardinalityContext.Direction.OUTGOING,
Optional.of(CardinalityContext.Operation.ADD)));
resultBuilder.addViolations(cardinalityRuleViolations);
}
return resultBuilder.build();
}
@Override
@SuppressWarnings("unchecked")
public CommandResult<RuleViolation> undo(final GraphCommandExecutionContext context) {
final SetConnectionSourceNodeCommand undoCommand = new SetConnectionSourceNodeCommand((Node<? extends View<?>, Edge>) getNode(context,
lastSourceNodeUUID),
getEdge(context),
lastMagnetIndex);
return undoCommand.execute(context);
}
@SuppressWarnings("unchecked")
public Node<? extends View<?>, Edge> getTargetNode(final GraphCommandExecutionContext context) {
if (null == targetNode) {
targetNode = getEdge(context).getTargetNode();
}
return targetNode;
}
@SuppressWarnings("unchecked")
public Node<? extends View<?>, Edge> getSourceNode(final GraphCommandExecutionContext context) {
if (null == sourceNode) {
sourceNode = (Node<? extends View<?>, Edge>) getNode(context,
sourceNodeUUID);
}
return sourceNode;
}
public Edge<? extends View, Node> getEdge(final GraphCommandExecutionContext context) {
if (null == this.edge) {
this.edge = getViewEdge(context,
edgeUUID);
}
return this.edge;
}
public Node<? extends View<?>, Edge> getSourceNode() {
return sourceNode;
}
public Edge<? extends View, Node> getEdge() {
return edge;
}
public Node<? extends View<?>, Edge> getTargetNode() {
return targetNode;
}
public Integer getMagnetIndex() {
return magnetIndex;
}
public Integer getLastSourceMagnetIndex() {
return lastMagnetIndex;
}
@Override
public String toString() {
return "SetConnectionSourceNodeCommand [edge=" + edgeUUID
+ ", candidate=" + (null != sourceNodeUUID ? sourceNodeUUID : "null")
+ ", magnet=" + magnetIndex + "]";
}
}