workcraft/StgPlugin/src/org/workcraft/plugins/stg/commands/ExpandHandshakeTransformationCommand.java
package org.workcraft.plugins.stg.commands;
import org.workcraft.commands.AbstractTransformationCommand;
import org.workcraft.commands.NodeTransformer;
import org.workcraft.dom.Container;
import org.workcraft.dom.visual.MixUtils;
import org.workcraft.dom.visual.TransformHelper;
import org.workcraft.dom.visual.VisualModel;
import org.workcraft.dom.visual.VisualNode;
import org.workcraft.dom.visual.connections.VisualConnection;
import org.workcraft.exceptions.InvalidConnectionException;
import org.workcraft.plugins.petri.VisualReadArc;
import org.workcraft.plugins.stg.Signal;
import org.workcraft.plugins.stg.SignalTransition;
import org.workcraft.plugins.stg.VisualSignalTransition;
import org.workcraft.plugins.stg.VisualStg;
import org.workcraft.types.Pair;
import org.workcraft.utils.*;
import org.workcraft.workspace.ModelEntry;
import org.workcraft.workspace.WorkspaceEntry;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
public class ExpandHandshakeTransformationCommand extends AbstractTransformationCommand implements NodeTransformer {
private static final String SUFFIX_REQ = "_req";
private static final String SUFFIX_ACK = "_ack";
private Pair<String, String> suffixPair = null;
@Override
public String getDisplayName() {
return "Expand selected handshake transitions...";
}
@Override
public String getPopupName() {
return "Expand handshake transition...";
}
@Override
public boolean isApplicableTo(WorkspaceEntry we) {
return WorkspaceUtils.isApplicable(we, VisualStg.class);
}
@Override
public boolean isApplicableTo(VisualNode node) {
return node instanceof VisualSignalTransition;
}
@Override
public boolean isEnabled(ModelEntry me, VisualNode node) {
return true;
}
@Override
public Position getPosition() {
return Position.TOP_MIDDLE;
}
@Override
public Collection<VisualNode> collectNodes(VisualModel model) {
Collection<VisualNode> signalTransitions = new HashSet<>();
if (model instanceof VisualStg) {
VisualStg stg = (VisualStg) model;
signalTransitions.addAll(stg.getVisualSignalTransitions());
signalTransitions.retainAll(stg.getSelection());
}
return signalTransitions;
}
@Override
public void transformNodes(VisualModel model, Collection<? extends VisualNode> nodes) {
if (model instanceof VisualStg) {
suffixPair = getSufixes();
if (suffixPair != null) {
for (VisualNode node: nodes) {
transformNode(model, node);
}
}
suffixPair = null;
}
}
@Override
public void transformNode(VisualModel model, VisualNode node) {
if ((model instanceof VisualStg) && (node instanceof VisualSignalTransition)) {
VisualStg stg = (VisualStg) model;
VisualSignalTransition transition = (VisualSignalTransition) node;
String ref = stg.getSignalReference(transition);
SignalTransition.Direction direction = transition.getDirection();
Container container = Hierarchy.getNearestContainer(transition);
String reqSuffix = SUFFIX_REQ;
String ackSuffix = SUFFIX_ACK;
if (suffixPair != null) {
reqSuffix = suffixPair.getFirst();
ackSuffix = suffixPair.getSecond();
}
VisualSignalTransition reqTransition = stg.createVisualSignalTransition(ref + reqSuffix, Signal.Type.OUTPUT, direction, container);
VisualSignalTransition ackTransition = stg.createVisualSignalTransition(ref + ackSuffix, Signal.Type.INPUT, direction, container);
Pair<Point2D, Point2D> positionPair = getReqAckPositions(stg, transition);
reqTransition.setRootSpacePosition(positionPair.getFirst());
ackTransition.setRootSpacePosition(positionPair.getSecond());
VisualConnection midConnection = null;
try {
midConnection = stg.connect(reqTransition, ackTransition);
for (VisualConnection connection: stg.getConnections(transition)) {
VisualNode predNode = connection.getFirst();
VisualNode succNode = connection.getSecond();
if (connection instanceof VisualReadArc) {
String predRef = stg.getMathReference(predNode);
String succRef = stg.getMathReference(succNode);
LogUtils.logWarning("Read-arc between '" + predRef + "' and '" + succRef + "' is ignored.");
continue;
}
if (transition == succNode) {
VisualConnection predConnection = stg.connect(predNode, reqTransition);
predConnection.copyShape(connection);
predConnection.copyStyle(connection);
}
if (transition == predNode) {
VisualConnection succConnection = stg.connect(ackTransition, succNode);
succConnection.copyStyle(connection);
succConnection.copyShape(connection);
}
}
} catch (InvalidConnectionException e) {
}
model.addToSelection(reqTransition);
model.addToSelection(ackTransition);
if (midConnection != null) {
model.addToSelection(midConnection);
}
stg.remove(transition);
}
}
private Pair<Point2D, Point2D> getReqAckPositions(VisualStg stg, VisualSignalTransition transition) {
LinkedList<Point2D> predPoints = new LinkedList<>();
LinkedList<Point2D> succPoints = new LinkedList<>();
for (VisualConnection connection: stg.getConnections(transition)) {
AffineTransform localToRootTransform = TransformHelper.getTransformToRoot(connection);
if (connection.getFirst() == transition) {
Point2D posInLocalSpace = connection.getPointOnConnection(1.0 / 3.0);
Point2D posInRootSpace = localToRootTransform.transform(posInLocalSpace, null);
succPoints.add(posInRootSpace);
}
if (connection.getSecond() == transition) {
Point2D posInLocalSpace = connection.getPointOnConnection(2.0 / 3.0);
Point2D posInRootSpace = localToRootTransform.transform(posInLocalSpace, null);
predPoints.add(posInRootSpace);
}
}
Point2D pos = transition.getRootSpacePosition();
if (predPoints.isEmpty() && succPoints.isEmpty()) {
predPoints.add(new Point2D.Double(pos.getX(), pos.getY() - 1.0));
succPoints.add(new Point2D.Double(pos.getX(), pos.getY() + 1.0));
} else if (predPoints.isEmpty()) {
Point2D succPoint = MixUtils.middlePoint(succPoints);
predPoints.add(new Point2D.Double(2.0 * pos.getX() - succPoint.getX(), 2.0 * pos.getY() - succPoint.getY()));
} else if (succPoints.isEmpty()) {
Point2D predPoint = MixUtils.middlePoint(predPoints);
succPoints.add(new Point2D.Double(2.0 * pos.getX() - predPoint.getX(), 2.0 * pos.getY() - predPoint.getY()));
}
Point2D predPoint = MixUtils.middlePoint(predPoints);
Point2D succPoint = MixUtils.middlePoint(succPoints);
return new Pair<>(predPoint, succPoint);
}
public Pair<String, String> getSufixes() {
Pair<String, String> result = null;
String ans = DialogUtils.showInput("Enter a pair of space-separated suffixes for handshake signals:",
SUFFIX_REQ + " " + SUFFIX_ACK);
if (ans != null) {
List<String> words = TextUtils.splitWords(ans);
if (words.size() == 2) {
result = Pair.of(words.get(0), words.get(1));
} else {
DialogUtils.showError("Two suffixes are required for handshake expansion.\n\n" +
"Default suffixes " + SUFFIX_REQ + " and " + SUFFIX_ACK + " will be used.");
result = null;
}
}
return result;
}
}