/*
* 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.client.canvas.command;
import java.util.logging.Logger;
import org.kie.workbench.common.stunner.core.client.canvas.AbstractCanvasHandler;
import org.kie.workbench.common.stunner.core.client.canvas.util.CanvasLayoutUtils;
import org.kie.workbench.common.stunner.core.client.command.CanvasViolation;
import org.kie.workbench.common.stunner.core.command.Command;
import org.kie.workbench.common.stunner.core.command.CommandResult;
import org.kie.workbench.common.stunner.core.command.impl.CompositeCommandImpl;
import org.kie.workbench.common.stunner.core.diagram.Diagram;
import org.kie.workbench.common.stunner.core.graph.Edge;
import org.kie.workbench.common.stunner.core.graph.Graph;
import org.kie.workbench.common.stunner.core.graph.Node;
import org.kie.workbench.common.stunner.core.graph.content.relationship.Child;
import org.kie.workbench.common.stunner.core.graph.content.relationship.Dock;
import org.kie.workbench.common.stunner.core.graph.content.view.View;
import org.kie.workbench.common.stunner.core.graph.processing.traverse.tree.AbstractTreeTraverseCallback;
import org.kie.workbench.common.stunner.core.graph.processing.traverse.tree.TreeWalkTraverseProcessor;
/**
* Base canvas command that adds a node and its children, if any, into the canvas, by aggregating
* canvas commands into a composite one, which finally is executed.
*/
public abstract class AbstractCanvasNodeRegistrationCommand extends AbstractCanvasCommand {
private static Logger LOGGER = Logger.getLogger(AbstractCanvasNodeRegistrationCommand.class.getName());
private final TreeWalkTraverseProcessor treeWalkTraverseProcessor;
private final Node node;
private Command<AbstractCanvasHandler, CanvasViolation> command;
protected AbstractCanvasNodeRegistrationCommand(final TreeWalkTraverseProcessor treeWalkTraverseProcessor,
final Node node) {
this.treeWalkTraverseProcessor = treeWalkTraverseProcessor;
this.node = node;
this.command = null;
}
protected abstract String getShapeSetId(final AbstractCanvasHandler context);
protected abstract boolean registerCandidate(final AbstractCanvasHandler context);
@Override
public CommandResult<CanvasViolation> execute(final AbstractCanvasHandler context) {
final Diagram diagram = context.getDiagram();
final String shapeSetId = null == getShapeSetId(context) ?
context.getDiagram().getMetadata().getShapeSetId() : getShapeSetId(context);
// Walk throw the graph and register the shapes.
treeWalkTraverseProcessor
.useEdgeVisitorPolicy(TreeWalkTraverseProcessor.EdgeVisitorPolicy.VISIT_EDGE_AFTER_TARGET_NODE)
.traverse(diagram.getGraph(),
node,
new AbstractTreeTraverseCallback<Graph, Node, Edge>() {
private CompositeCommandImpl.CompositeCommandBuilder<AbstractCanvasHandler, CanvasViolation> commandBuilder;
@Override
public void startGraphTraversal(final Graph graph) {
command = null;
commandBuilder = new CompositeCommandImpl.CompositeCommandBuilder<AbstractCanvasHandler, CanvasViolation>().forward();
}
@Override
@SuppressWarnings("unchecked")
public boolean startNodeTraversal(final Node node) {
if (CanvasLayoutUtils.isCanvasRoot(diagram,
node)) {
// Canvas root gets not bind to any shape, so no need to execute
// any canvas registration command for it.
return true;
}
// Delegate to subtypes the candidate node registering.
if (null != AbstractCanvasNodeRegistrationCommand.this.node
&& AbstractCanvasNodeRegistrationCommand.this.node.equals(node)) {
return registerCandidate(context);
}
// Register only visible and candidate's child nodes. The
// execution of the AddCanvasNodeCommand will traverse only its children
// as well, an so on recursively.
if (node.getContent() instanceof View && isChild(node)) {
commandBuilder.addCommand(new AddCanvasNodeCommand(node,
shapeSetId));
return true;
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public boolean startEdgeTraversal(final Edge edge) {
final Object content = edge.getContent();
if (content instanceof View) {
// Add the commands that register view connectors.
commandBuilder.addCommand(new AddCanvasConnectorCommand(edge,
shapeSetId));
return true;
} else if (content instanceof Child) {
final Node child = edge.getTargetNode();
final Node parent = edge.getSourceNode();
final Object childContent = child.getContent();
if (childContent instanceof View) {
commandBuilder.addCommand(new SetCanvasChildNodeCommand(parent,
child));
}
return true;
} else if (content instanceof Dock) {
final Node docked = edge.getTargetNode();
final Node parent = edge.getSourceNode();
final Object dockedContent = docked.getContent();
if (dockedContent instanceof View) {
commandBuilder.addCommand(new CanvasDockNodeCommand(parent,
docked));
}
return true;
}
return false;
}
@Override
public void endGraphTraversal() {
super.endGraphTraversal();
// BUild a composite command that aggregates the other ones, if any registered.
if (commandBuilder.size() > 0) {
command = commandBuilder.build();
}
}
@SuppressWarnings("unchecked")
private boolean isChild(final Node<?, Edge> candidate) {
return null == getCandidate() ||
candidate.getInEdges().stream()
.filter(edge -> {
if (edge.getContent() instanceof Child) {
final Node<?, Edge> parent = edge.getSourceNode();
return null != parent &&
(parent.equals(getCandidate()) || isChild(parent));
}
return false;
})
.findFirst()
.isPresent();
}
});
if (null != command) {
return command.execute(context);
}
return buildResult();
}
public Node getCandidate() {
return node;
}
}