/* * 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.client.lienzo.canvas.controls; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import com.ait.lienzo.client.core.shape.wires.WiresContainer; import com.ait.lienzo.client.core.shape.wires.WiresShape; import com.google.gwt.logging.client.LogConfiguration; import org.kie.workbench.common.stunner.client.lienzo.canvas.wires.WiresCanvas; import org.kie.workbench.common.stunner.client.lienzo.canvas.wires.WiresUtils; import org.kie.workbench.common.stunner.core.client.canvas.AbstractCanvasHandler; import org.kie.workbench.common.stunner.core.client.canvas.controls.CanvasControl; import org.kie.workbench.common.stunner.core.client.command.CanvasCommandManager; import org.kie.workbench.common.stunner.core.client.command.CanvasViolation; import org.kie.workbench.common.stunner.core.client.command.RequiresCommandManager; 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.CompositeCommand; import org.kie.workbench.common.stunner.core.command.impl.CompositeCommandImpl; 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.Node; import org.kie.workbench.common.stunner.core.graph.content.relationship.Child; public abstract class AbstractContainmentBasedControl implements CanvasControl<AbstractCanvasHandler>, RequiresCommandManager<AbstractCanvasHandler> { private static Logger LOGGER = Logger.getLogger(AbstractContainmentBasedControl.class.getName()); private AbstractCanvasHandler canvasHandler; private CommandManagerProvider<AbstractCanvasHandler> commandManagerProvider; protected abstract void doEnable(final WiresCanvas.View view); protected abstract void doDisable(final WiresCanvas.View view); protected abstract boolean isEdgeAccepted(final Edge edge); protected abstract Command<AbstractCanvasHandler, CanvasViolation> getAddEdgeCommand(final Node parent, final Node child); protected abstract Command<AbstractCanvasHandler, CanvasViolation> getDeleteEdgeCommand(final Node parent, final Node child); @Override public void enable(final AbstractCanvasHandler canvasHandler) { this.canvasHandler = canvasHandler; final WiresCanvas.View canvasView = (WiresCanvas.View) canvasHandler.getAbstractCanvas().getView(); doEnable(canvasView); } @Override public void disable() { if (null != canvasHandler && null != canvasHandler.getCanvas()) { final WiresCanvas.View canvasView = (WiresCanvas.View) canvasHandler.getAbstractCanvas().getView(); doDisable(canvasView); } this.canvasHandler = null; this.commandManagerProvider = null; } @Override public void setCommandManagerProvider(final CommandManagerProvider<AbstractCanvasHandler> provider) { this.commandManagerProvider = provider; } @SuppressWarnings("unchecked") public boolean allow(final Node parent, final Node child) { if (parent == null && child == null) { return false; } boolean isAllow = true; final Edge edge = getTheEdge(child); final boolean isSameParent = isSameParent(parent, edge); if (!isSameParent) { final CommandResult<CanvasViolation> violations = runAllow(parent, child, edge); isAllow = isAccept(child, violations); } return isAllow; } @SuppressWarnings("unchecked") public boolean accept(final Node parent, final Node child) { if (parent == null && child == null) { return false; } final Edge edge = getTheEdge(child); final boolean isSameParent = isSameParent(parent, edge); boolean isAccept = true; if (!isSameParent) { final CommandResult<CanvasViolation> violations = runAccept(parent, child, edge); isAccept = isAccept(child, violations); } return isAccept; } protected boolean isAccept(final WiresContainer wiresContainer, final WiresShape wiresShape) { if (!isEnabled() || !WiresUtils.isWiresShape(wiresContainer) || !WiresUtils.isWiresShape(wiresShape)) { return false; } return true; } @SuppressWarnings("unchecked") protected CommandResult<CanvasViolation> runAllow(final Node parent, final Node child, final Edge edge) { final CompositeCommand<AbstractCanvasHandler, CanvasViolation> command = buildCommands(parent, child, edge); return getCommandManager().allow(canvasHandler, command); } @SuppressWarnings("unchecked") protected CommandResult<CanvasViolation> runAccept(final Node parent, final Node child, final Edge edge) { final CompositeCommand<AbstractCanvasHandler, CanvasViolation> command = buildCommands(parent, child, edge); return getCommandManager().execute(canvasHandler, command); } protected AbstractCanvasHandler getCanvasHandler() { return canvasHandler; } protected CanvasCommandManager<AbstractCanvasHandler> getCommandManager() { return commandManagerProvider.getCommandManager(); } private CompositeCommand<AbstractCanvasHandler, CanvasViolation> buildCommands(final Node parent, final Node child, final Edge edge) { // Remove current relationship, if any. final boolean hasSourceNode = null != edge && null != edge.getSourceNode(); final Command<AbstractCanvasHandler, CanvasViolation> deleteEdgeCommand = hasSourceNode ? getDeleteEdgeCommand(edge.getSourceNode(), child) : null; // Add a new relationship, if any. final boolean hasNewTarget = null != parent; final Command<AbstractCanvasHandler, CanvasViolation> addEdgeCommand = hasNewTarget ? getAddEdgeCommand(parent, child) : null; final CompositeCommandImpl.CompositeCommandBuilder<AbstractCanvasHandler, CanvasViolation> commandBuilder = new CompositeCommandImpl .CompositeCommandBuilder<AbstractCanvasHandler, CanvasViolation>() .reverse(); if (null != deleteEdgeCommand) { commandBuilder.addCommand(deleteEdgeCommand); } if (null != addEdgeCommand) { commandBuilder.addCommand(addEdgeCommand); } return commandBuilder.build(); } private boolean isEnabled() { return canvasHandler != null; } private boolean isSameParent(final Node parent, final Edge<Child, Node> edge) { if (null != edge) { final Node sourceNode = edge.getSourceNode(); if (null != sourceNode) { final String parentUUID = null != parent ? parent.getUUID() : canvasHandler.getDiagram().getMetadata().getCanvasRootUUID(); return null != parentUUID && sourceNode.getUUID().equals(parentUUID); } } return parent == null; } @SuppressWarnings("unchecked") private Edge<Object, Node> getTheEdge(final Node child) { if (child != null) { final List<Edge> outEdges = child.getInEdges(); if (null != outEdges && !outEdges.isEmpty()) { for (final Edge edge : outEdges) { if (isEdgeAccepted(edge)) { return edge; } } } } return null; } private boolean isAccept(final Node candidate, final CommandResult<CanvasViolation> result) { return !CommandUtils.isError(result); } private void log(final Level level, final String message) { if (LogConfiguration.loggingIsEnabled()) { LOGGER.log(level, message); } } }