/* * 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.LinkedList; import java.util.List; 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.Command; import org.kie.workbench.common.stunner.core.command.CommandResult; import org.kie.workbench.common.stunner.core.command.util.CommandUtils; 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.command.GraphCommandExecutionContext; import org.kie.workbench.common.stunner.core.graph.command.GraphCommandResultBuilder; import org.kie.workbench.common.stunner.core.graph.content.definition.Definition; 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.util.SafeDeleteNodeProcessor; 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.impl.RuleContextBuilder; import org.uberfire.commons.validation.PortablePreconditions; /** * Deletes a node taking into account its ingoing / outgoing edges and safe remove all node's children as well, if any. */ @Portable public final class SafeDeleteNodeCommand extends AbstractGraphCompositeCommand { private String candidateUUID; private transient Node<?, Edge> node; private transient SafeDeleteNodeProcessor.Callback safeDeleteCallback; public SafeDeleteNodeCommand(final @MapsTo("candidateUUID") String candidateUUID) { this.candidateUUID = PortablePreconditions.checkNotNull("candidateUUID", candidateUUID); this.safeDeleteCallback = null; } public SafeDeleteNodeCommand(final Node<?, Edge> node) { this(node.getUUID()); this.node = node; } public SafeDeleteNodeCommand(final Node<?, Edge> node, final SafeDeleteNodeProcessor.Callback safeDeleteCallback) { this(node); this.safeDeleteCallback = safeDeleteCallback; } @Override @SuppressWarnings("unchecked") protected SafeDeleteNodeCommand initialize(final GraphCommandExecutionContext context) { super.initialize(context); final Node<Definition<?>, Edge> candidate = (Node<Definition<?>, Edge>) getCandidate(context); // Delete & set incoming & outgoing edges for the node being deleted. final List<Command<GraphCommandExecutionContext, RuleViolation>> commands = new LinkedList<>(); new SafeDeleteNodeProcessor(candidate).run(new SafeDeleteNodeProcessor.Callback() { @Override public void deleteChildNode(final Node<Definition<?>, Edge> node) { commands.add(new SafeDeleteNodeCommand(node, safeDeleteCallback).initialize(context)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteChildNode(node); } } @Override public void deleteInViewEdge(final Edge<View<?>, Node> edge) { commands.add(new SetConnectionTargetNodeCommand(null, edge)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteInViewEdge(edge); } } @Override public void deleteInChildEdge(final Edge<Child, Node> edge) { final Node parent = edge.getSourceNode(); commands.add(new RemoveChildCommand(parent, candidate)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteInChildEdge(edge); } } @Override public void deleteInDockEdge(final Edge<Dock, Node> edge) { final Node parent = edge.getSourceNode(); commands.add(new UnDockNodeCommand(parent, candidate)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteInDockEdge(edge); } } @Override public void deleteOutViewEdge(final Edge<? extends View<?>, Node> edge) { commands.add(new SetConnectionSourceNodeCommand(null, edge)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteOutViewEdge(edge); } } @Override public void deleteNode(final Node<Definition<?>, Edge> node) { commands.add(new DeregisterNodeCommand(node)); if (null != safeDeleteCallback) { safeDeleteCallback.deleteNode(node); } } }); // Add the commands above as composited. for (Command<GraphCommandExecutionContext, RuleViolation> command : commands) { SafeDeleteNodeCommand.this.addCommand(command); } return this; } @Override protected boolean delegateRulesContextToChildren() { return true; } @Override @SuppressWarnings("unchecked") protected CommandResult<RuleViolation> doAllow(final GraphCommandExecutionContext context, final Command<GraphCommandExecutionContext, RuleViolation> command) { final CommandResult<RuleViolation> result = super.doAllow(context, command); if (!CommandUtils.isError(result) && hasRules(context)) { final Graph target = getGraph(context); final Node<View<?>, Edge> candidate = (Node<View<?>, Edge>) getCandidate(context); final GraphCommandResultBuilder builder = new GraphCommandResultBuilder(); final Collection<RuleViolation> cardinalityRuleViolations = doEvaluate(context, RuleContextBuilder.GraphContexts.cardinality(target, Optional.of(candidate), Optional.of(CardinalityContext.Operation.DELETE))); builder.addViolations(cardinalityRuleViolations); for (final RuleViolation violation : cardinalityRuleViolations) { if (builder.isError(violation)) { return builder.build(); } } return builder.build(); } return result; } @Override public CommandResult<RuleViolation> undo(final GraphCommandExecutionContext context) { return super.undo(context, true); } @SuppressWarnings("unchecked") private Node<? extends Definition<?>, Edge> getCandidate(final GraphCommandExecutionContext context) { if (null == node) { node = checkNodeNotNull(context, candidateUUID); } return (Node<View<?>, Edge>) node; } public Node<?, Edge> getNode() { return node; } private boolean hasRules(final GraphCommandExecutionContext context) { return null != context.getRuleManager(); } @Override public String toString() { return "SafeDeleteNodeCommand [candidate=" + candidateUUID + "]"; } }