diff options
Diffstat (limited to 'plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/support/FSMSupportUtil.java')
-rw-r--r-- | plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/support/FSMSupportUtil.java | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/support/FSMSupportUtil.java b/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/support/FSMSupportUtil.java new file mode 100644 index 000000000..c9e625113 --- /dev/null +++ b/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/support/FSMSupportUtil.java @@ -0,0 +1,958 @@ +/******************************************************************************* + * 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.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.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.ContextSwitcher; +import org.eclipse.etrice.ui.behavior.fsm.support.IPositionProvider; +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.context.impl.AddConnectionContext; +import org.eclipse.graphiti.features.context.impl.AddContext; +import org.eclipse.graphiti.features.context.impl.LayoutContext; +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(StateGraphItem item, ContainerShape cs) { + EObject container = Graphiti.getLinkService().getBusinessObjectForLinkedPictogramElement(cs); + if (container instanceof StateGraph) { + StateGraph sg = (StateGraph) container; + return item.eContainer()!=sg; + } + else if (container instanceof State) { + assert(item instanceof EntryPoint || item instanceof ExitPoint): "this MUST be an entry or exit point"; + + // have to check whether the State is inherited + State s = (State) container; + ContainerShape sCont = cs.getContainer(); + EObject cls = sCont.getLink().getBusinessObjects().get(0); + return s.eContainer()!=cls; + } + + return false; + } + + public boolean isInherited(Diagram diag, EObject obj) { + return fsmHelpers.getModelComponent(obj)!=getModelComponent(diag); + } + + 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 ac + * @param targetContainer + * @param fp + * @return + */ + public StateGraph insertRefinedState(StateGraph sg, ModelComponent ac, ContainerShape targetContainer, + IFeatureProvider fp) { + sg = getSubGraphOfRefinedStateFor((State) sg.eContainer(), ac); + fp.link(targetContainer, sg); + return sg; + } + + /** + * @param sg + * @param ac + * @param targetContainer + */ + public void undoInsertRefinedState(StateGraph sg, ModelComponent ac, + ContainerShape targetContainer, IFeatureProvider fp) { + RefinedState rs = (RefinedState) sg.eContainer(); + fp.link(targetContainer, rs.getTarget().getSubgraph()); + + if (!(fsmHelpers.hasDetailCode(rs.getEntryCode()) || fsmHelpers.hasDetailCode(rs.getExitCode()))) { + ac.getStateMachine().getStates().remove(rs); + } + } + + /** + * @param s + * @param ac + * @return + */ + public StateGraph getSubGraphOfRefinedStateFor(State s, ModelComponent ac) { + RefinedState rs = getRefinedStateFor(s, ac); + + if (rs.getSubgraph()==null) + rs.setSubgraph(FSMFactory.eINSTANCE.createStateGraph()); + + return rs.getSubgraph(); + } + + public RefinedState getRefinedStateFor(State s, ModelComponent ac) { + HashMap<State, RefinedState> target2rs = new HashMap<State, RefinedState>(); + for (State st : ac.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 = ac.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 ac = getModelComponent(diagram); + return fsmHelpers.getTargettingState(state, ac); + } + + /** + * 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 ac the ModelComponent + * @param diagram the current diagram + * @param fp the feature provider + */ + public void deleteSubStructureRecursive(State s, ModelComponent ac, + Diagram diagram, IFeatureProvider fp) { + if (fsmHelpers.hasSubStructure(s, ac)) { + StateGraph subgraph = s.getSubgraph(); + + // depth first + for (State st : subgraph.getStates()) { + deleteSubStructureRecursive(st, ac, 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>(); + for (State item : expected) { + if (present.contains(item)) + toUpdate.add(item); + else + toAdd.add(item); + } + addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor); + updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), 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>(); + for (TrPoint item : expected) { + if (present.contains(item)) + toUpdate.add(item); + else + toAdd.add(item); + } + addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor); + updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), 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>(); + for (ChoicePoint item : expected) { + if (present.contains(item)) + toUpdate.add(item); + else + toAdd.add(item); + } + addStateGraphNodes(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor); + updateStateGraphNodes(toUpdate, shapes, ctx.getPositionProvider(), 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>(); + for (Transition trans : expected) + if (!present.containsKey(trans)) + toAdd.add(trans); + + addTransitions(toAdd, ctx.getPositionProvider(), sgShape, fp, node2anchor); + updateTransitions(present, ctx.getPositionProvider(), sgShape, fp, node2anchor); + } + } + + /** + * @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; + } + } + } + } + + 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; + } + +} |