blob: 12ba3d7a563d30b556da52af9916c3975a499cfb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 protos software gmbh (http://www.protos.de).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* CONTRIBUTORS:
* Henrik Rentz-Reichert (initial contribution)
*
*******************************************************************************/
package org.eclipse.etrice.ui.behavior.fsm.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.etrice.core.fsm.fSM.ChoicePoint;
import org.eclipse.etrice.core.fsm.fSM.ChoicepointTerminal;
import org.eclipse.etrice.core.fsm.fSM.EntryPoint;
import org.eclipse.etrice.core.fsm.fSM.ExitPoint;
import org.eclipse.etrice.core.fsm.fSM.FSMFactory;
import org.eclipse.etrice.core.fsm.fSM.InitialTransition;
import org.eclipse.etrice.core.fsm.fSM.ModelComponent;
import org.eclipse.etrice.core.fsm.fSM.NonInitialTransition;
import org.eclipse.etrice.core.fsm.fSM.RefinedState;
import org.eclipse.etrice.core.fsm.fSM.RefinedTransition;
import org.eclipse.etrice.core.fsm.fSM.State;
import org.eclipse.etrice.core.fsm.fSM.StateGraph;
import org.eclipse.etrice.core.fsm.fSM.StateGraphItem;
import org.eclipse.etrice.core.fsm.fSM.StateGraphNode;
import org.eclipse.etrice.core.fsm.fSM.StateTerminal;
import org.eclipse.etrice.core.fsm.fSM.SubStateTrPointTerminal;
import org.eclipse.etrice.core.fsm.fSM.TrPoint;
import org.eclipse.etrice.core.fsm.fSM.TrPointTerminal;
import org.eclipse.etrice.core.fsm.fSM.Transition;
import org.eclipse.etrice.core.fsm.fSM.TransitionTerminal;
import org.eclipse.etrice.core.fsm.naming.FSMNameProvider;
import org.eclipse.etrice.core.fsm.ui.FSMUiModule;
import org.eclipse.etrice.core.fsm.util.FSMHelpers;
import org.eclipse.etrice.core.fsm.util.FSMUtil;
import org.eclipse.etrice.core.fsm.validation.FSMValidationUtil;
import org.eclipse.etrice.ui.behavior.fsm.commands.StateGraphContext;
import org.eclipse.etrice.ui.behavior.fsm.support.IPositionProvider.Pos;
import org.eclipse.etrice.ui.behavior.fsm.support.IPositionProvider.PosAndSize;
import org.eclipse.etrice.ui.common.base.support.CommonSupportUtil;
import org.eclipse.graphiti.datatypes.ILocation;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IRemoveFeature;
import org.eclipse.graphiti.features.context.IRemoveContext;
import org.eclipse.graphiti.features.context.impl.AddConnectionContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.LayoutContext;
import org.eclipse.graphiti.features.context.impl.RemoveContext;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.FreeFormConnection;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.IGaService;
import org.eclipse.graphiti.services.ILinkService;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
* @author Henrik Rentz-Reichert
*
*/
public class FSMSupportUtil {
private static final String INITIAL = "init";
private static final String STATE = "state:";
private static final String TP = "tp:";
private static final String CP = "cp:";
private static final String SEP = ".";
private static FSMSupportUtil instance = null;
/**
* @return the instance
*/
public static FSMSupportUtil getInstance() {
if (instance==null) {
// this class has members that have to be filled by the FSMUi injector
Injector injector = FSMUiModule.getInjector();
instance = injector.getInstance(FSMSupportUtil.class);
}
return instance;
}
@Inject
private FSMHelpers fsmHelpers;
@Inject
private FSMValidationUtil fsmValidationUtil;
@Inject
private FSMNameProvider fsmNameProvider;
@Inject
private FSMUtil fsmUtil;
/**
* @return the roomHelpers
*/
public FSMHelpers getFSMHelpers() {
return fsmHelpers;
}
/**
* @return the validationUtil
*/
public FSMValidationUtil getFSMValidationUtil() {
return fsmValidationUtil;
}
/**
* @return the roomUtil
*/
public FSMUtil getFSMUtil() {
return fsmUtil;
}
/**
* @return the roomNameProvider
*/
public FSMNameProvider getFSMNameProvider() {
return fsmNameProvider;
}
public EObject getOwnObject(EObject obj, ResourceSet rs) {
URI uri = EcoreUtil.getURI(obj);
EObject own = rs.getEObject(uri, true);
assert(own!=null): "own object must exist";
return own;
}
public boolean isInherited(Diagram diag, EObject obj) {
if(obj instanceof StateGraph)
obj = obj.eContainer();
return obj instanceof RefinedState || obj instanceof RefinedTransition || !EcoreUtil.isAncestor(getModelComponent(diag), obj);
}
public boolean showAsInherited(Diagram diag, State obj) {
if (obj instanceof RefinedState)
return true;
return fsmHelpers.getModelComponent(obj)!=getModelComponent(diag);
}
public Diagram getDiagram(GraphicsAlgorithm ga) {
if (ga.eContainer() instanceof GraphicsAlgorithm)
return getDiagram((GraphicsAlgorithm)ga.eContainer());
return getDiagram(ga.getPictogramElement());
}
/**
* @param pictogramElement
* @return
*/
public Diagram getDiagram(PictogramElement pe) {
while (pe.eContainer()!=null) {
if (pe.eContainer() instanceof Diagram)
return (Diagram) pe.eContainer();
pe = (PictogramElement) pe.eContainer();
}
return null;
}
public ModelComponent getModelComponent(Diagram diag) {
EObject bo = Graphiti.getLinkService().getBusinessObjectForLinkedPictogramElement(diag);
if (bo instanceof ModelComponent)
return (ModelComponent) bo;
return null;
}
/**
* @param sg
* @param mc
* @param targetContainer
* @param fp
* @return
*/
public StateGraph insertRefinedState(StateGraph sg, ModelComponent mc, ContainerShape targetContainer,
IFeatureProvider fp) {
sg = getSubGraphOfRefinedStateFor((State) sg.eContainer(), mc);
fp.link(targetContainer, sg);
return sg;
}
/**
* @param sg
* @param mc
* @param targetContainer
*/
public void undoInsertRefinedState(StateGraph sg, ModelComponent mc,
ContainerShape targetContainer, IFeatureProvider fp) {
RefinedState rs = (RefinedState) sg.eContainer();
fp.link(targetContainer, rs.getTarget().getSubgraph());
if (!(fsmHelpers.hasDetailCode(rs.getEntryCode()) || fsmHelpers.hasDetailCode(rs.getExitCode()))) {
mc.getStateMachine().getStates().remove(rs);
}
}
/**
* @param s
* @param mc
* @return
*/
public StateGraph getSubGraphOfRefinedStateFor(State s, ModelComponent mc) {
RefinedState rs = getRefinedStateFor(s, mc);
if (rs.getSubgraph()==null)
rs.setSubgraph(FSMFactory.eINSTANCE.createStateGraph());
return rs.getSubgraph();
}
public RefinedState getRefinedStateFor(State s, ModelComponent mc) {
HashMap<State, RefinedState> target2rs = new HashMap<State, RefinedState>();
for (State st : mc.getStateMachine().getStates()) {
if (st instanceof RefinedState)
target2rs.put(((RefinedState) st).getTarget(), (RefinedState) st);
}
RefinedState rs = null;
// do we already have a RefinedState pointing to s?
if (target2rs.containsKey(s)) {
rs = target2rs.get(s);
}
else {
// we have to create one and place it in the best fitting context
StateGraph sg = null;
State parent = s;
while (parent.eContainer().eContainer() instanceof State) {
parent = (State) s.eContainer().eContainer();
if (target2rs.containsKey(parent)) {
RefinedState bestFitting = target2rs.get(parent);
if (bestFitting.getSubgraph()==null)
bestFitting.setSubgraph(FSMFactory.eINSTANCE.createStateGraph());
sg = bestFitting.getSubgraph();
break;
}
}
if (sg==null)
sg = mc.getStateMachine();
rs = FSMFactory.eINSTANCE.createRefinedState();
rs.setTarget(s);
sg.getStates().add(rs);
}
return rs;
}
/**
* @param state
* @param diagram
* @return
*/
public State getTargettingState(State state, Diagram diagram) {
ModelComponent mc = getModelComponent(diagram);
return fsmHelpers.getTargettingState(state, mc);
}
/**
* This method exploits the fact that the immediate children of the diagram are
* associated with the state graphs.
*
* @param shape
* @return the container shape that is associated with the state graph of the diagram
*/
public ContainerShape getStateGraphContainer(ContainerShape shape) {
while (shape!=null) {
ContainerShape parent = shape.getContainer();
if (parent instanceof Diagram)
return shape;
shape = parent;
}
return null;
}
public StateGraph getStateGraph(ContainerShape cs, IFeatureProvider fp) {
ContainerShape shape = getStateGraphContainer(cs);
Object bo = fp.getBusinessObjectForPictogramElement(shape);
if (bo instanceof StateGraph)
return (StateGraph) bo;
else
assert(false): "state graph expected";
return null;
}
public TransitionTerminal getTransitionTerminal(Anchor anchor, IFeatureProvider fp) {
if (anchor != null) {
Object obj = fp.getBusinessObjectForPictogramElement(anchor.getParent());
if (obj instanceof TrPoint) {
Object parent = fp.getBusinessObjectForPictogramElement((ContainerShape) anchor.getParent().eContainer());
if (parent instanceof State) {
State state = (parent instanceof RefinedState)? ((RefinedState)parent).getTarget() : (State)parent;
SubStateTrPointTerminal sstpt = FSMFactory.eINSTANCE.createSubStateTrPointTerminal();
sstpt.setState(state);
sstpt.setTrPoint((TrPoint) obj);
return sstpt;
}
else {
TrPointTerminal tpt = FSMFactory.eINSTANCE.createTrPointTerminal();
tpt.setTrPoint((TrPoint) obj);
return tpt;
}
}
else if (obj instanceof State) {
State state = (obj instanceof RefinedState)? ((RefinedState)obj).getTarget() : (State)obj;
StateTerminal st = FSMFactory.eINSTANCE.createStateTerminal();
st.setState(state);
return st;
}
else if (obj instanceof ChoicePoint) {
ChoicepointTerminal ct = FSMFactory.eINSTANCE.createChoicepointTerminal();
ct.setCp((ChoicePoint) obj);
return ct;
}
}
return null;
}
public boolean isInitialPoint(Anchor anchor, IFeatureProvider fp) {
if (anchor!=null) {
Object obj = fp.getBusinessObjectForPictogramElement(anchor.getParent());
if (obj instanceof StateGraph) {
Object parent = fp.getBusinessObjectForPictogramElement((ContainerShape) anchor.getParent().eContainer());
if (parent instanceof StateGraph)
return true;
}
}
return false;
}
public boolean canConnect(Anchor asrc, Anchor atgt, ContainerShape cs, IFeatureProvider fp) {
return canConnect(asrc, atgt, null, cs, fp);
}
public boolean canConnect(Anchor asrc, Anchor atgt, Transition trans,
ContainerShape cs, IFeatureProvider fp) {
TransitionTerminal src = getTransitionTerminal(asrc, fp);
TransitionTerminal tgt = getTransitionTerminal(atgt, fp);
if (src==null && !isInitialPoint(asrc, fp))
return false;
if (tgt==null)
return false;
StateGraph sg = getStateGraph(cs, fp);
if (sg==null)
return false;
return fsmValidationUtil.isConnectable(src, tgt, trans, sg).isOk();
}
/**
* @param s the state whose sub structure should be deleted
* @param mc the ModelComponent
* @param diagram the current diagram
* @param fp the feature provider
*/
public void deleteSubStructureRecursive(State s, ModelComponent mc,
Diagram diagram, IFeatureProvider fp) {
if (fsmHelpers.hasSubStructure(s, mc)) {
StateGraph subgraph = s.getSubgraph();
// depth first
for (State st : subgraph.getStates()) {
deleteSubStructureRecursive(st, mc, diagram, fp);
}
ContainerShape subShape = ContextSwitcher.getContext(diagram, subgraph);
CommonSupportUtil.deleteConnectionsRecursive(subShape, fp);
EcoreUtil.delete(subShape, true);
}
}
public List<State> getStates(ContainerShape shape, IFeatureProvider fp) {
return getStates(shape, fp, null, null);
}
private List<State> getStates(ContainerShape shape, IFeatureProvider fp, Map<String, Anchor> item2anchor, List<Shape> stateShapes) {
List<State> items = new ArrayList<State>();
for (Shape ch : shape.getChildren()) {
Object bo = fp.getBusinessObjectForPictogramElement(ch);
if (bo instanceof State) {
items.add((State)bo);
if (item2anchor!=null)
item2anchor.put(getKey((State)bo), ch.getAnchors().get(0));
if (stateShapes!=null)
stateShapes.add(ch);
}
}
return items;
}
public List<ChoicePoint> getChoicePoints(ContainerShape shape, IFeatureProvider fp) {
return getChoicePoints(shape, fp, null, null);
}
private List<ChoicePoint> getChoicePoints(ContainerShape shape, IFeatureProvider fp, Map<String, Anchor> item2anchor,
List<Shape> cpShapes) {
List<ChoicePoint> items = new ArrayList<ChoicePoint>();
for (Shape ch : shape.getChildren()) {
Object bo = fp.getBusinessObjectForPictogramElement(ch);
if (bo instanceof ChoicePoint) {
items.add((ChoicePoint)bo);
if (item2anchor!=null)
item2anchor.put(getKey((ChoicePoint)bo), ch.getAnchors().get(0));
if (cpShapes!=null)
cpShapes.add(ch);
}
}
return items;
}
public List<TrPoint> getTrPoints(StateGraph sg, ContainerShape shape, IFeatureProvider fp) {
return getTrPoints(sg, shape, fp, null, null);
}
private List<TrPoint> getTrPoints(StateGraph sg, ContainerShape shape, IFeatureProvider fp,
Map<String, Anchor> item2anchor, List<Shape> tpShapes) {
List<TrPoint> items = new ArrayList<TrPoint>();
for (Shape ch : shape.getChildren()) {
Object bo = fp.getBusinessObjectForPictogramElement(ch);
if (bo instanceof TrPoint) {
items.add((TrPoint)bo);
if (item2anchor!=null)
item2anchor.put(getKey((TrPoint)bo), ch.getAnchors().get(0));
if (tpShapes!=null)
tpShapes.add(ch);
}
}
return items;
}
/**
* @param diagram
* @param fp
* @return
*/
public List<Transition> getTransitions(Diagram diagram, IFeatureProvider fp) {
List<Transition> transitions = new ArrayList<Transition>();
for (Connection conn : diagram.getConnections()) {
Object bo = fp.getBusinessObjectForPictogramElement(conn);
if (bo instanceof Transition)
transitions.add((Transition) bo);
}
return transitions;
}
private Map<Transition, Connection> getTransitionsMap(ContainerShape sgShape, IFeatureProvider fp) {
Diagram diagram = (Diagram) sgShape.eContainer();
Map<Transition, Connection> transitions = new HashMap<Transition, Connection>();
for (Connection conn : diagram.getConnections()) {
Object bo = fp.getBusinessObjectForPictogramElement(conn);
// we only collect connections that have a starting point contained in our sgShape
if (bo instanceof Transition && EcoreUtil.isAncestor(sgShape, conn.getStart()))
transitions.put((Transition) bo, conn);
}
return transitions;
}
/**
* @param sgShape
* @param node2anchor
*/
private void getSubTpAnchors(ContainerShape sgShape, HashMap<String, Anchor> node2anchor) {
for (Shape childShape : sgShape.getChildren()) {
EObject bo = Graphiti.getLinkService().getBusinessObjectForLinkedPictogramElement(childShape);
if (bo instanceof State)
getAnchors((State) bo, childShape, node2anchor);
}
}
public ContainerShape addStateGraph(StateGraphContext ctx, Diagram diagram, IFeatureProvider fp) {
AddContext addContext = new AddContext();
addContext.setNewObject(ctx.getStateGraph());
addContext.setTargetContainer(diagram);
PosAndSize graphPosAndSize = ctx.getPositionProvider().getGraphPosAndSize(ctx.getStateGraph());
if (graphPosAndSize!=null) {
addContext.setX(graphPosAndSize.getX());
addContext.setY(graphPosAndSize.getY());
addContext.setWidth(graphPosAndSize.getWidth());
addContext.setHeight(graphPosAndSize.getHeight());
}
else {
addContext.setX(StateGraphSupport.MARGIN);
addContext.setY(StateGraphSupport.MARGIN);
}
ContainerShape sgShape = (ContainerShape) fp.addIfPossible(addContext);
if (sgShape==null)
return null;
final HashMap<String, Anchor> node2anchor = new HashMap<String, Anchor>();
GraphicsAlgorithm borderRect = sgShape.getGraphicsAlgorithm().getGraphicsAlgorithmChildren().get(0);
ctx.getPositionProvider().setScale(borderRect.getWidth(), borderRect.getHeight());
ctx.getPositionProvider().setPosition(sgShape.getGraphicsAlgorithm().getX(), sgShape.getGraphicsAlgorithm().getY());
addInitialPointIff(ctx, ctx.getPositionProvider(), sgShape, fp, node2anchor);
addStateGraphNodes(ctx.getTrPoints(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
addStateGraphNodes(ctx.getStates(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
addStateGraphNodes(ctx.getChPoints(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
for (StateGraphContext sub : ctx.getChildren()) {
addStateGraph(sub, diagram, fp);
}
getSubTpAnchors(sgShape, node2anchor);
addTransitions(ctx.getTransitions(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
return sgShape;
}
private void addInitialPointIff(StateGraphContext ctx, IPositionProvider positionProvider, ContainerShape sgShape,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
// model
StateGraph sg = ctx.getInitialPoint();
if(sg==null)
// (super class) diagram
sg = positionProvider.getInitialPoint(ctx.getStateGraph());
if(sg==null)
return;
PosAndSize pos = positionProvider.getPosition(sg);
AddContext addContext = new AddContext();
addContext.setNewObject(sg);
addContext.setTargetContainer(sgShape);
if(pos != null){
addContext.setX(pos.getX());
addContext.setY(pos.getY());
if (pos.getWidth()>0 && pos.getHeight()>0) {
addContext.setWidth(pos.getWidth());
addContext.setHeight(pos.getHeight());
}
} else {
addContext.setX(3*StateGraphSupport.MARGIN);
addContext.setY(3*StateGraphSupport.MARGIN);
}
ContainerShape pe = (ContainerShape) fp.addIfPossible(addContext);
assert(pe!=null): "initial point should have been created";
assert(!pe.getAnchors().isEmpty()): "initial point should have an anchor";
node2anchor.put(INITIAL, pe.getAnchors().get(0));
}
public void updateStateGraph(StateGraph sg, StateGraphContext ctx, ContainerShape sgShape,
IFeatureProvider fp) {
HashMap<String, Anchor> node2anchor = new HashMap<String, Anchor>();
GraphicsAlgorithm borderRect = sgShape.getGraphicsAlgorithm().getGraphicsAlgorithmChildren().get(0);
ctx.getPositionProvider().setScale(borderRect.getWidth(), borderRect.getHeight());
ctx.getPositionProvider().setPosition(sgShape.getGraphicsAlgorithm().getX(), sgShape.getGraphicsAlgorithm().getY());
// states
{
ArrayList<Shape> shapes = new ArrayList<Shape>();
List<State> present = getStates(sgShape, fp, node2anchor, shapes);
checkDuplicates(present);
List<State> expected = ctx.getStates();
List<State> toAdd = new ArrayList<State>();
List<State> toUpdate = new ArrayList<State>();
List<State> toRemove = new ArrayList<State>();
for (State item : expected) {
if (present.contains(item))
toUpdate.add(item);
else
toAdd.add(item);
}
for(State item : present){
if(!expected.contains(item))
toRemove.add(item);
}
addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor);
updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), fp);
removeGraphicalRepresentation(toRemove, shapes, fp);
}
// transition points
{
ArrayList<Shape> shapes = new ArrayList<Shape>();
List<TrPoint> present = getTrPoints(sg, sgShape, fp, node2anchor, shapes);
checkDuplicates(present);
List<TrPoint> expected = ctx.getTrPoints();
List<TrPoint> toAdd = new ArrayList<TrPoint>();
List<TrPoint> toUpdate = new ArrayList<TrPoint>();
List<TrPoint> toRemove = new ArrayList<TrPoint>();
for (TrPoint item : expected) {
if (present.contains(item))
toUpdate.add(item);
else
toAdd.add(item);
}
for(TrPoint item : present){
if(!expected.contains(item))
toRemove.add(item);
}
addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor);
updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), fp);
removeGraphicalRepresentation(toRemove, shapes, fp);
}
// choice points
{
ArrayList<Shape> shapes = new ArrayList<Shape>();
List<ChoicePoint> present = getChoicePoints(sgShape, fp, node2anchor, shapes);
checkDuplicates(present);
List<ChoicePoint> expected = ctx.getChPoints();
List<ChoicePoint> toAdd = new ArrayList<ChoicePoint>();
List<ChoicePoint> toUpdate = new ArrayList<ChoicePoint>();
List<ChoicePoint> toRemove = new ArrayList<ChoicePoint>();
for (ChoicePoint item : expected) {
if (present.contains(item))
toUpdate.add(item);
else
toAdd.add(item);
}
for(ChoicePoint item : present){
if(!expected.contains(item))
toRemove.add(item);
}
addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor);
updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), fp);
removeGraphicalRepresentation(toRemove, shapes, fp);
}
getSubTpAnchors(sgShape, node2anchor);
// initial point
{
// exists in this diagram ?
Shape present = null;
for (Shape ch : sgShape.getChildren()) {
Object bo = fp.getBusinessObjectForPictogramElement(ch);
if (bo instanceof StateGraph)
present = ch;
}
if(present != null)
node2anchor.put(INITIAL, present.getAnchors().get(0));
// exists in model ?
StateGraph expected = ctx.getInitialPoint();
if(expected == null)
// exists in (super class) diagram ?
expected = ctx.getPositionProvider().getInitialPoint(ctx.getStateGraph());
if(expected != null && present == null)
addInitialPointIff(ctx, ctx.getPositionProvider(), sgShape, fp, node2anchor);
else
updateInitialPoint(present, ctx.getPositionProvider(), fp);
}
// transitions
{
// get transitions that belong to our state graph
// (for other connections we might not have the node anchors yet)
Map<Transition, Connection> present = getTransitionsMap(sgShape, fp);
List<Transition> expected = ctx.getTransitions();
List<Transition> toAdd = new ArrayList<Transition>();
List<Transition> toRemove = new ArrayList<Transition>();
for (Transition trans : expected)
if (!present.containsKey(trans))
toAdd.add(trans);
for(Transition item : present.keySet()){
if(!expected.contains(item))
toRemove.add(item);
}
addTransitions(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor);
updateTransitions(present, ctx.getPositionProvider(), sgShape, fp, node2anchor);
removeGraphicalRepresentation(toRemove, present.values(), fp);
}
}
/**
* @param items
*/
private void checkDuplicates(List<? extends StateGraphItem> items) {
for (StateGraphItem item : items) {
if (items.indexOf(item)!=items.lastIndexOf(item)) {
Assert.isTrue(
items.indexOf(item)==items.lastIndexOf(item),
"multiple occurrences of "+fsmNameProvider.getFullPath(item));
}
}
}
private void addTransitions(List<Transition> transitions, IPositionProvider positionProvider, ContainerShape sgShape,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
for (Transition trans : transitions) {
String from = (trans instanceof InitialTransition)? INITIAL:getKey(((NonInitialTransition)trans).getFrom());
String to = getKey(trans.getTo());
Anchor src = node2anchor.get(from);
Anchor dst = node2anchor.get(to);
assert(src!=null && dst!=null): "transition endpoints must be present";
AddConnectionContext context = new AddConnectionContext(src, dst);
context.setNewObject(trans);
PictogramElement pe = fp.addIfPossible(context);
if (pe instanceof FreeFormConnection) {
FreeFormConnection conn = (FreeFormConnection) pe;
// JH: workaround for correct bend points of inherited self transition
conn.getBendpoints().clear();
List<Pos> points = positionProvider.getPoints(trans);
if (points!=null && !points.isEmpty()) {
Iterator<Pos> it = points.iterator();
// first is label position
Pos pos = it.next();
ConnectionDecorator cd = conn.getConnectionDecorators().get(1);
Graphiti.getGaService().setLocation(cd.getGraphicsAlgorithm(), pos.getX(), pos.getY());
// remaining are bend points
while (it.hasNext()) {
pos = it.next();
Point pt = Graphiti.getGaService().createPoint(pos.getX(), pos.getY());
conn.getBendpoints().add(pt);
}
}
else if (src==dst) {
ILocation begin = Graphiti.getPeService().getLocationRelativeToDiagram(conn.getStart());
Point pt = Graphiti.getGaService().createPoint(begin.getX(), begin.getY()+StateGraphSupport.MARGIN*3);
conn.getBendpoints().add(pt);
}
}
}
}
private void addStateGraphNodes(List<? extends StateGraphNode> nodes, IPositionProvider positionProvider, ContainerShape sgShape,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
List<PosAndSize> positions = positionProvider.getPositions(nodes);
int idx = 0;
for (StateGraphNode node : nodes) {
addStateGraphNode(node, sgShape, positions.get(idx), fp, node2anchor);
++idx;
}
}
private void addStateGraphNode(StateGraphNode tp, ContainerShape sgShape, PosAndSize pos,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
AddContext addContext = new AddContext();
addContext.setNewObject(tp);
addContext.setTargetContainer(sgShape);
addContext.setX(pos.getX());
addContext.setY(pos.getY());
if (pos.getWidth()>0 && pos.getHeight()>0) {
addContext.setWidth(pos.getWidth());
addContext.setHeight(pos.getHeight());
}
ContainerShape pe = (ContainerShape) fp.addIfPossible(addContext);
assert(pe!=null): tp.eClass().getName()+" should have been created";
assert(!pe.getAnchors().isEmpty()): tp.eClass().getName()+" should have an anchor";
node2anchor.put(getKey(tp), pe.getAnchors().get(0));
}
private void updateInitialPoint(Shape shape, IPositionProvider positionProvider, IFeatureProvider fp) {
if (shape==null)
return;
StateGraph sg = (StateGraph) fp.getBusinessObjectForPictogramElement(shape);
PosAndSize ps = positionProvider.getPosition(sg);
if (ps==null)
return;
// relocate and resize the invisible rectangle
GraphicsAlgorithm ga = shape.getGraphicsAlgorithm();
Graphiti.getLayoutService().setLocationAndSize(
ga,
ps.getX(),
ps.getY(),
ps.getWidth(),
ps.getHeight()
);
// have to call the layout to adjust the visible border
LayoutContext lc = new LayoutContext(shape);
fp.layoutIfPossible(lc);
}
private void updateStateGraphNodes(List<? extends StateGraphNode> nodes, List<Shape> shapes,
IPositionProvider positionProvider, IFeatureProvider fp) {
ILinkService linkService = Graphiti.getLinkService();
IGaService gaService = Graphiti.getGaService();
for (StateGraphNode node : nodes) {
PosAndSize ps = positionProvider.getPosition(node);
if (ps==null)
continue;
// TODO: sub-optimal since quadratic effort - use combined list for nodes and shapes or similar solution
for (Shape shape : shapes) {
EObject bo = linkService.getBusinessObjectForLinkedPictogramElement(shape);
if (bo==node) {
// relocate and resize the invisible rectangle
GraphicsAlgorithm ga = shape.getGraphicsAlgorithm();
// System.out.println(RoomNameProvider.getFullPath(node)+": "+ga.getX()+" "+ga.getY()+" "+ga.getWidth()+" "+ga.getHeight());
// System.out.println(" -> "+ps.getX()+" "+ps.getY()+" "+ps.getWidth()+" "+ps.getHeight());
int margin = 0;
if (node instanceof State)
margin = StateSupport.MARGIN;
else if (node instanceof TrPoint)
margin = TrPointSupport.MARGIN;
gaService.setLocationAndSize(
ga,
ps.getX()-margin,
ps.getY()-margin,
ps.getWidth()+2*margin,
ps.getHeight()+2*margin
);
// have to call the layout to adjust the visible border
LayoutContext lc = new LayoutContext(shape);
fp.layoutIfPossible(lc);
break;
}
}
}
}
public void removeGraphicalRepresentation(Collection<? extends EObject> toRemove, Collection<? extends PictogramElement> pictograms, IFeatureProvider fp) {
for(EObject bo : toRemove)
for(PictogramElement pe : pictograms)
if(fp.getBusinessObjectForPictogramElement(pe) == bo){
IRemoveContext rc = new RemoveContext(pe);
IRemoveFeature removeFeature = fp.getRemoveFeature(rc);
if (removeFeature != null)
removeFeature.remove(rc);
break;
}
}
private void updateTransitions(Map<Transition, Connection> transitions, IPositionProvider positionProvider, ContainerShape sgShape,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
for(Entry<Transition, Connection> e: transitions.entrySet()){
Transition trans = e.getKey();
Connection conn = e.getValue();
String from = (trans instanceof InitialTransition)? INITIAL:getKey(((NonInitialTransition)trans).getFrom());
String to = getKey(trans.getTo());
Anchor newSrc = node2anchor.get(from);
Anchor newDst = node2anchor.get(to);
assert(newSrc!=null && newDst!=null): "transition endpoints must be present";
if(conn.getStart()!=newSrc)
conn.setStart(newSrc);
if(conn.getEnd()!=newDst)
conn.setEnd(newDst);
List<Pos> points = positionProvider.getPoints(trans);
Iterator<Pos> it = points.iterator();
if (points==null || points.isEmpty())
continue;
// first is label position
Pos pos = it.next();
ConnectionDecorator cd = conn.getConnectionDecorators().get(1);
Graphiti.getGaService().setLocation(cd.getGraphicsAlgorithm(), pos.getX(), pos.getY());
if (conn instanceof FreeFormConnection) {
FreeFormConnection fconn = (FreeFormConnection) conn;
// remaining are bend points
fconn.getBendpoints().clear();
List<Point> bendpoints = new ArrayList<Point>();
while (it.hasNext()) {
pos = it.next();
Point pt = Graphiti.getGaService().createPoint(pos.getX(), pos.getY());
bendpoints.add(pt);
}
fconn.getBendpoints().addAll(bendpoints);
}
}
}
private void getAnchors(State state, PictogramElement stateShape, final HashMap<String, Anchor> node2anchor) {
if (stateShape instanceof ContainerShape) {
node2anchor.put(getKey(state), ((ContainerShape)stateShape).getAnchors().get(0));
for (Shape child : ((ContainerShape) stateShape).getChildren()) {
if (child instanceof ContainerShape) {
ContainerShape childShape = (ContainerShape) child;
if (!childShape.getAnchors().isEmpty()) {
if (!childShape.getLink().getBusinessObjects().isEmpty()) {
EObject obj = childShape.getLink().getBusinessObjects().get(0);
if (obj instanceof EntryPoint || obj instanceof ExitPoint) {
node2anchor.put(getKey(obj, true), childShape.getAnchors().get(0));
}
}
}
}
}
}
}
private String getKey(EObject obj) {
return getKey(obj, false);
}
private String getKey(EObject obj, boolean subTp) {
if (obj instanceof TrPoint) {
TrPoint tp = (TrPoint) obj;
if (!subTp)
return TP+tp.getName();
else {
if (tp.eContainer().eContainer() instanceof State) {
State s = (State) tp.eContainer().eContainer();
return TP+tp.getName()+SEP+s.getName();
}
else {
assert(false): "State expected";
}
}
}
else if (obj instanceof State) {
return STATE+((State)obj).getName();
}
else if (obj instanceof ChoicePoint) {
return CP+((ChoicePoint)obj).getName();
}
else if (obj instanceof TransitionTerminal) {
TransitionTerminal tt = (TransitionTerminal) obj;
if (tt instanceof ChoicepointTerminal) {
return CP+((ChoicepointTerminal)tt).getCp().getName();
}
else if (tt instanceof StateTerminal) {
return STATE+((StateTerminal)tt).getState().getName();
}
else if (tt instanceof SubStateTrPointTerminal) {
SubStateTrPointTerminal sstt = (SubStateTrPointTerminal) tt;
return TP+sstt.getTrPoint().getName()+SEP+sstt.getState().getName();
}
else if (tt instanceof TrPointTerminal) {
return TP+((TrPointTerminal)tt).getTrPoint().getName();
}
else {
assert(false): "unexpected sub type";
}
}
assert(false): "unexpected type";
return null;
}
}