/*
* Copyright 2016 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.processing.traverse.content;
import java.util.List;
import java.util.Optional;
import java.util.Stack;
import java.util.stream.Collectors;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
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.view.View;
import org.kie.workbench.common.stunner.core.graph.processing.traverse.tree.TreeWalkTraverseProcessor;
@Dependent
public final class ChildrenTraverseProcessorImpl extends AbstractContentTraverseProcessor<Child, Node<View, Edge>, Edge<Child, Node>, ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>>>
implements ChildrenTraverseProcessor {
private final ParentStack parentStack = new ParentStack();
@Inject
public ChildrenTraverseProcessorImpl(final TreeWalkTraverseProcessor treeWalkTraverseProcessor) {
super(treeWalkTraverseProcessor);
treeWalkTraverseProcessor.useStartNodePredicate(node -> !node.getInEdges().stream()
.filter(e -> e.getContent() instanceof Child)
.findAny()
.isPresent());
}
@Override
public ChildrenTraverseProcessor setRootUUID(final String rootUUID) {
parentStack.setRootUUID(rootUUID);
return this;
}
@Override
protected TreeWalkTraverseProcessor.EdgeVisitorPolicy getPolicy() {
return TreeWalkTraverseProcessor.EdgeVisitorPolicy.VISIT_EDGE_BEFORE_TARGET_NODE;
}
@Override
protected void doStartGraphTraversal(final Graph graph,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
parentStack.clear();
}
@Override
@SuppressWarnings("unchecked")
protected boolean doStartEdgeTraversal(final Edge edge,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
if (accepts(edge)) {
final Node<View, Edge> parent = edge.getSourceNode();
parentStack.push(parent);
callback.startEdgeTraversal(edge);
return true;
}
return false;
}
@Override
protected boolean doEndEdgeTraversal(final Edge edge,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
if (accepts(edge)) {
parentStack.pop();
callback.endEdgeTraversal(edge);
return true;
}
return false;
}
@Override
protected void doEndGraphTraversal(final Graph graph,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
callback.endGraphTraversal();
parentStack.clear();
}
@Override
@SuppressWarnings("unchecked")
protected boolean doStartNodeTraversal(final Node node,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
if (!parentStack.isRootDefined() || parentStack.isRootPresent()) {
return fireNodeTraverseCallback(node,
callback);
}
return true;
}
@SuppressWarnings("unchecked")
private boolean fireNodeTraverseCallback(final Node node,
final ChildrenTraverseCallback<Node<View, Edge>, Edge<Child, Node>> callback) {
if (!parentStack.isEmpty()) {
return callback.startNodeTraversal(parentStack.asList(),
node);
} else {
callback.startNodeTraversal(node);
return true;
}
}
@Override
protected boolean accepts(final Edge edge) {
return edge.getContent() instanceof Child;
}
private class ParentStack {
private final Stack<Node<View, Edge>> stack = new Stack<>();
private Optional<String> rootUUID;
private boolean hasParent;
public ParentStack() {
this.rootUUID = Optional.empty();
this.hasParent = false;
}
public void setRootUUID(final String uuid) {
this.rootUUID = Optional.ofNullable(uuid);
}
public Node<View, Edge> push(final Node<View, Edge> item) {
if (isRootUUID(item)) {
hasParent = true;
}
return stack.push(item);
}
public Node<View, Edge> peek() {
return stack.peek();
}
public Node<View, Edge> pop() {
final Node<View, Edge> pop = stack.pop();
if (isRootUUID(pop)) {
hasParent = false;
}
return pop;
}
public void clear() {
hasParent = false;
stack.clear();
}
public boolean isRootDefined() {
return this.rootUUID.isPresent();
}
public boolean isRootPresent() {
return hasParent;
}
public boolean isEmpty() {
return stack.isEmpty();
}
public List<Node<View, Edge>> asList() {
return stack.stream()
.collect(Collectors.toList());
}
private boolean isRootUUID(final Node node) {
return rootUUID.isPresent() &&
null != node &&
node.getUUID().equals(rootUUID.get());
}
}
}