/** * Copyright (c) 2013 committers of YAKINDU and others. * 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: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.sct.refactoring.refactor.impl; import static org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil.createInlineStyle; import static org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil.getInlineStyle; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; import org.eclipse.gmf.runtime.diagram.core.services.ViewService; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.notation.BooleanValueStyle; import org.eclipse.gmf.runtime.notation.Bounds; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.Style; import org.eclipse.gmf.runtime.notation.View; import org.yakindu.sct.model.sgraph.Entry; import org.yakindu.sct.model.sgraph.Exit; import org.yakindu.sct.model.sgraph.ReactionProperty; import org.yakindu.sct.model.sgraph.Region; import org.yakindu.sct.model.sgraph.SGraphFactory; import org.yakindu.sct.model.sgraph.State; import org.yakindu.sct.model.sgraph.Statechart; import org.yakindu.sct.model.sgraph.Transition; import org.yakindu.sct.model.sgraph.Vertex; import org.yakindu.sct.model.stext.stext.EntryPointSpec; import org.yakindu.sct.model.stext.stext.ExitPointSpec; import org.yakindu.sct.model.stext.stext.StextFactory; import org.yakindu.sct.refactoring.refactor.AbstractRefactoring; import org.yakindu.sct.ui.editor.DiagramActivator; import org.yakindu.sct.ui.editor.editor.StatechartDiagramEditor; import org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil; import org.yakindu.sct.ui.editor.providers.SemanticHints; /** * * @author andreas muelder - Initial contribution and API * */ public class ExtractSubdiagramRefactoring extends AbstractRefactoring<View> { protected PreferencesHint preferencesHint = DiagramActivator.DIAGRAM_PREFERENCES_HINT; private Diagram subdiagram; @Override public boolean isExecutable() { EObject element = getContextObject().getElement(); if (!(element instanceof State)) { return false; } State state = (State) element; BooleanValueStyle inlineStyle = getInlineStyle(getContextObject()); return super.isExecutable() && state.isComposite() && (inlineStyle == null || inlineStyle.isBooleanValue()); } @Override protected void internalExecute() { setNotationStyle(); subdiagram = createSubdiagram(); createEntryExitPoints(subdiagram); setPreferredSize(); } @Override protected boolean internalDoUndo() { // close the sub diagram before undo will delete it return DiagramPartitioningUtil.closeSubdiagramEditors((State) subdiagram.getElement()); } @SuppressWarnings("unchecked") protected void createEntryExitPoints(Diagram subdiagram) { TreeIterator<EObject> eAllContents = subdiagram.eAllContents(); List<Edge> entryPointsToCreate = new ArrayList<Edge>(); List<Edge> exitPointstoCreate = new ArrayList<Edge>(); while (eAllContents.hasNext()) { EObject next = eAllContents.next(); if (next instanceof View) { EList<Edge> targetEdges = ((View) next).getTargetEdges(); for (Edge edge : targetEdges) { if (!EcoreUtil.isAncestor(subdiagram, edge.getSource())) entryPointsToCreate.add(edge); } EList<Edge> sourceEdges = ((View) next).getSourceEdges(); for (Edge edge : sourceEdges) { if (!EcoreUtil.isAncestor(subdiagram, edge.getTarget())) exitPointstoCreate.add(edge); } } } for (Edge edge : entryPointsToCreate) { createEntryPoint(edge, subdiagram); } for (Edge edge : exitPointstoCreate) { createExitPoint(edge, subdiagram); } } protected void createEntryPoint(Edge edge, Diagram subdiagram) { Transition transition = (Transition) edge.getElement(); Region entryPointContainer = getEntryPointContainer(transition); Entry entryPoint = createSemanticEntryPoint(transition); // re-wire old transition to targeting the selected state transition.setTarget((State) subdiagram.getElement()); View oldTarget = edge.getTarget(); edge.setTarget(getContextObject()); // create node for entry point View entryPointContainerView = helper.getViewForSemanticElement(entryPointContainer, subdiagram); View entryPointRegionCompartment = ViewUtil.getChildBySemanticHint(entryPointContainerView, SemanticHints.REGION_COMPARTMENT); Node entryNode = ViewService.createNode(entryPointRegionCompartment, entryPoint, SemanticHints.ENTRY, preferencesHint); ViewService.createEdge(entryNode, oldTarget, entryPoint.getOutgoingTransitions().get(0), SemanticHints.TRANSITION, preferencesHint); addEntryPointSpec(transition, entryPoint); } private void addEntryPointSpec(Transition transition, Entry entryPoint) { EList<ReactionProperty> properties = transition.getProperties(); EntryPointSpec entryPointSpec = StextFactory.eINSTANCE.createEntryPointSpec(); // A transition can only have one entry point so alter the existing for (ReactionProperty reactionProperty : properties) { if (reactionProperty instanceof EntryPointSpec) { entryPointSpec = (EntryPointSpec) reactionProperty; } } entryPointSpec.setEntrypoint(entryPoint.getName()); properties.add(entryPointSpec); } private void addExitPointSpec(Transition transition, Exit exitPoint) { EList<ReactionProperty> properties = transition.getProperties(); ExitPointSpec exitPointSpec = StextFactory.eINSTANCE.createExitPointSpec(); // A transition can only have one exit point so alter the existing for (ReactionProperty reactionProperty : properties) { if (reactionProperty instanceof ExitPointSpec) { exitPointSpec = (ExitPointSpec) reactionProperty; } } exitPointSpec.setExitpoint(exitPoint.getName()); properties.add(exitPointSpec); } protected String getEntryPointName(Transition transition) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("entry_"); stringBuilder.append(transition.getTarget().getName()); int index = transition.getSource().getOutgoingTransitions().indexOf(transition); stringBuilder.append(index); return asIdentifier(stringBuilder.toString()); } protected String getExitPointName(Transition transition) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("exit_"); stringBuilder.append(transition.getSource().getName()); int index = transition.getSource().getOutgoingTransitions().indexOf(transition); stringBuilder.append(index); return asIdentifier(stringBuilder.toString()); } protected Entry createSemanticEntryPoint(Transition transition) { Region entryPointTarget = getEntryPointContainer(transition); String name = getEntryPointName(transition); Entry entryPoint = null; Iterator<Vertex> iterator = entryPointTarget.getVertices().iterator(); while (iterator.hasNext()) { Vertex next = iterator.next(); if (next instanceof Entry) { Entry current = (Entry) next; if (name.equals(current.getName())) { // Do nothing, there already exists an entry point return current; } } } entryPoint = SGraphFactory.eINSTANCE.createEntry(); entryPoint.setName(name); entryPointTarget.getVertices().add(entryPoint); Transition entryPointTransition = SGraphFactory.eINSTANCE.createTransition(); entryPointTransition.setSource(entryPoint); entryPointTransition.setTarget(transition.getTarget()); return entryPoint; } private Exit createSemanticExitPoint(Transition transition) { Region exitPointContainer = getExitPointContainer(transition); String name = getExitPointName(transition); Exit exitPoint = null; Iterator<Vertex> iterator = exitPointContainer.getVertices().iterator(); while (iterator.hasNext()) { Vertex next = iterator.next(); if (next instanceof Exit) { Exit current = (Exit) next; if (name.equals(current.getName())) { // Do nothing, there already exists an entry point return current; } } } exitPoint = SGraphFactory.eINSTANCE.createExit(); exitPoint.setName(name); exitPointContainer.getVertices().add(exitPoint); return exitPoint; } private Region getEntryPointContainer(Transition transition) { // entry point container is the subdiagram's state's region which // contains the transition target EObject firstParentRegion = transition.getTarget().getParentRegion(); return getOutermostParentRegion(firstParentRegion); } private Region getExitPointContainer(Transition transition) { // exit point container is the subdiagram's state's region which // contains the transition source EObject firstParentRegion = transition.getSource().getParentRegion(); return getOutermostParentRegion(firstParentRegion); } private Region getOutermostParentRegion(EObject element) { while (!(element.eContainer() instanceof Statechart)) { EObject container = element.eContainer(); if (container instanceof State) { State parentState = (State) container; if (parentState.equals(subdiagram.getElement())) { return (Region) element; } element = parentState.getParentRegion(); } else { element = element.eContainer(); } } return null; } protected void createExitPoint(Edge edge, Diagram subdiagram) { Transition transition = (Transition) edge.getElement(); // create semantic exit point Region exitPointContainer = getExitPointContainer(transition); Exit exitPoint = createSemanticExitPoint(transition); // create node for exit point View exitPointContainerView = helper.getViewForSemanticElement(exitPointContainer, subdiagram); View exitPointRegionCompartment = ViewUtil.getChildBySemanticHint(exitPointContainerView, SemanticHints.REGION_COMPARTMENT); Node exitNode = ViewService.createNode(exitPointRegionCompartment, exitPoint, SemanticHints.EXIT, preferencesHint); // re-wire existing transition to new exit point Vertex oldTransitionTarget = transition.getTarget(); transition.setTarget(exitPoint); View oldEdgeTarget = edge.getTarget(); edge.setTarget(exitNode); // create transition from selected state to former transition target Transition exitPointTransition = SGraphFactory.eINSTANCE.createTransition(); exitPointTransition.setSource((State) subdiagram.getElement()); exitPointTransition.setTarget(oldTransitionTarget); ViewService.createEdge(getContextObject(), oldEdgeTarget, exitPointTransition, SemanticHints.TRANSITION, preferencesHint); addExitPointSpec(exitPointTransition, exitPoint); } /** * Sets the GMF inline {@link Style} to true */ @SuppressWarnings("unchecked") protected void setNotationStyle() { BooleanValueStyle inlineStyle = getInlineStyle(getContextObject()); if (inlineStyle == null) { inlineStyle = createInlineStyle(); getContextObject().getStyles().add(inlineStyle); } inlineStyle.setBooleanValue(false); } /** * Creates a new {@link Diagram} and copies child elements */ protected Diagram createSubdiagram() { View contextView = getContextObject(); State contextElement = (State) contextView.getElement(); Diagram subdiagram = ViewService.createDiagram(contextElement, StatechartDiagramEditor.ID, preferencesHint); View figureCompartment = ViewUtil.getChildBySemanticHint(contextView, SemanticHints.STATE_FIGURE_COMPARTMENT); getResource().getContents().add(subdiagram); while (figureCompartment.getChildren().size() > 0) { subdiagram.insertChild((View) figureCompartment.getChildren().get(0)); } return subdiagram; } protected void setPreferredSize() { Node node = (Node) getContextObject(); Bounds bounds = (Bounds) node.getLayoutConstraint(); bounds.setWidth(-1); bounds.setHeight(-1); } }