/**
* 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 java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.yakindu.base.expressions.expressions.Expression;
import org.yakindu.sct.model.sgraph.Effect;
import org.yakindu.sct.model.sgraph.Scope;
import org.yakindu.sct.model.sgraph.State;
import org.yakindu.sct.model.sgraph.Transition;
import org.yakindu.sct.model.stext.stext.EntryEvent;
import org.yakindu.sct.model.stext.stext.LocalReaction;
import org.yakindu.sct.model.stext.stext.ReactionEffect;
import org.yakindu.sct.model.stext.stext.ReactionTrigger;
import org.yakindu.sct.model.stext.stext.StextFactory;
import org.yakindu.sct.refactoring.refactor.AbstractRefactoring;
/**
* Implementation for 'fold incoming actions' refactoring. This refactoring
* moves actions of incoming transitions to the entry block of a state. Actions
* can only be moved if they are used at all incoming transitions in the same
* order (checked from back to front).
* <br><br>
* Context:
* <ul>
* <li>Exactly one state.</li>
* </ul>
* Preconditions:
* <ul>
* <li>At least one action is foldable.</li>
* <li>No incoming transition enters a composite state which has entry actions.</li>
* </ul>
* @author thomas kutz - Initial contribution and API
*
*/
public class FoldIncomingActionsRefactoring extends AbstractRefactoring<State> {
@Override
protected void internalExecute() {
List<Expression> actionsToFold = getFoldableActions();
addActionsToEntryReaction(actionsToFold);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isExecutable() {
return super.isExecutable()
&& atLeastOneActionIsFoldable()
&& noIncomingTransitionEntersCompositeWithEntryActions();
}
private boolean noIncomingTransitionEntersCompositeWithEntryActions() {
return !helper.oneIncomingTransitionEntersCompositeWithEntryActions(getContextObject());
}
private boolean atLeastOneActionIsFoldable() {
EList<Transition> transitions = getContextObject().getIncomingTransitions();
return getLastFoldableAction(helper.getAllActions(transitions), 0) != null;
}
private List<Expression> getFoldableActions() {
EList<Transition> transitions = getContextObject()
.getIncomingTransitions();
List<Expression> foldableActions = new ArrayList<Expression>();
Expression lastFoldableAction;
int indexFromBack = 0;
while ((lastFoldableAction = getLastFoldableAction(
helper.getAllActions(transitions), indexFromBack)) != null) {
foldableActions.add(0, lastFoldableAction);
indexFromBack++;
}
removeLastActions(transitions, indexFromBack);
return foldableActions;
}
private void addActionsToEntryReaction(final List<Expression> actionsToAdd) {
if (actionsToAdd.isEmpty()) {
return;
}
EList<Expression> actionsOriginal = helper
.getFirstEntryActions(getContextObject());
if (actionsOriginal == null) {
actionsOriginal = createEntryBlock();
}
actionsOriginal.addAll(actionsToAdd);
}
private EList<Expression> createEntryBlock() {
EList<Expression> actionsOriginal;
LocalReaction newLocalReaction = StextFactory.eINSTANCE
.createLocalReaction();
ReactionTrigger newReactionTrigger = StextFactory.eINSTANCE
.createReactionTrigger();
EntryEvent entryEvent = StextFactory.eINSTANCE.createEntryEvent();
ReactionEffect newReactionEffect = StextFactory.eINSTANCE
.createReactionEffect();
newLocalReaction.setTrigger(newReactionTrigger);
newReactionTrigger.getTriggers().add(entryEvent);
newLocalReaction.setEffect(newReactionEffect);
Scope scope = getContextObject().getScopes().get(0);
scope.getReactions().add(newLocalReaction);
actionsOriginal = newReactionEffect.getActions();
return actionsOriginal;
}
private void removeLastActions(EList<Transition> transitions, int number) {
for (Transition transition : transitions) {
List<Expression> actionsToRemove = getLastActions(transition,
number);
for (Expression action : actionsToRemove) {
EcoreUtil.delete(action);
}
// delete transition's effect when no more actions left
Effect effect = transition.getEffect();
if (!helper.hasAtLeastOneAction(transition) && effect != null) {
EcoreUtil.delete(transition.getEffect());
}
}
}
private List<Expression> getLastActions(Transition transition, int number) {
List<Expression> lastActions = new ArrayList<Expression>();
Effect effect = transition.getEffect();
if (effect instanceof ReactionEffect) {
ReactionEffect reactionEffect = (ReactionEffect) effect;
List<Expression> actions = reactionEffect.getActions();
for (int i = 1; i <= number; i++) {
lastActions.add(actions.get(actions.size() - i));
}
}
return lastActions;
}
private Expression getLastFoldableAction(
List<List<Expression>> allActions, int indexFromBack) {
Expression actionToCheck = null;
for (List<Expression> actionList : allActions) {
if (actionList.size() - 1 - indexFromBack < 0) {
return null;
}
Expression lastAction = actionList.get(actionList.size() - 1
- indexFromBack);
if (actionToCheck == null) {
actionToCheck = lastAction;
} else if (!EcoreUtil.equals(actionToCheck, lastAction)) {
return null;
}
}
return actionToCheck;
}
@Override
protected String getCommandLabel() {
return "Fold Incoming Actions";
}
}