-
Ludovic Apvrille authoredLudovic Apvrille authored
AvatarStateMachine.java 91.73 KiB
/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille
*
* ludovic.apvrille AT enst.fr
*
* This software is a computer program whose purpose is to allow the
* edition of TURTLE analysis, design and deployment diagrams, to
* allow the generation of RT-LOTOS or Java code from this diagram,
* and at last to allow the analysis of formal validation traces
* obtained from external tools, e.g. RTL from LAAS-CNRS and CADP
* from INRIA Rhone-Alpes.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package avatartranslator;
import avatartranslator.intboolsolver.AvatarIBSolver;
import myutil.MyMath;
import myutil.TraceManager;
import java.util.*;
/**
* Class AvatarStateMachine
* State machine, with composite states
* Creation: 20/05/2010
*
* @author Ludovic APVRILLE
* @version 1.0 20/05/2010
*/
public class AvatarStateMachine extends AvatarElement {
private static int ID_ELT = 0;
protected List<AvatarStateMachineElement> elements;
protected AvatarStartState startState;
protected AvatarStateMachineOwner block;
// Built to gather information on states
protected List<AvatarStateMachineElement> states;
// To be used by code generator for fast access to states
public AvatarStateElement[] allStates;
public AvatarStateMachine(AvatarStateMachineOwner _block, String _name, Object _referenceObject) {
super(_name, _referenceObject);
block = _block;
elements = new LinkedList<AvatarStateMachineElement>();
}
public AvatarStateMachineOwner getOwner() {
return block;
}
public AvatarStartState getStartState() {
return startState;
}
public void setStartState(AvatarStartState _state) {
startState = _state;
}
public int getNbOfStatesElement() {
int cpt = 0;
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarStateElement) {
cpt++;
}
}
return cpt;
}
public void clear() {
elements.clear();
startState = null;
allStates = null;
states = null;
}
public void makeBasicSM(AvatarStateMachineOwner owner) {
elements.clear();
if (startState == null) {
startState = new AvatarStartState("StartState", null, owner);
} else {
startState.removeAllNexts();
}
addElement(startState);
AvatarTransition at = new AvatarTransition(owner, "Transition", startState.getReferenceObject());
AvatarStopState stopS = new AvatarStopState("StopState", startState.getReferenceObject(), owner);
addElement(at);
addElement(stopS);
startState.addNext(at);
at.addNext(stopS);
}
public boolean isBasicStateMachine() {
if (startState == null) {
return true;
}
if (elements.size() > 3) {
return false;
}
boolean hasStartState = false, hasStopState = false, hasBasicTransition = false;
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarStartState) {
hasStartState = true;
}
if (asme instanceof AvatarStartState) {
hasStopState = true;
}
if (asme instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) asme;
if (at.isEmpty()) {
hasBasicTransition = true;
}
}
}
return hasStartState && hasStopState && hasBasicTransition;
}
/**
* Make sure that there is a start state, a stop state and that all
* elements apart from regular states are followed by a stop or a next
*/
public void makeCorrect(AvatarStateMachineOwner owner) {
if (startState == null) {
TraceManager.addDev("Null start state");
makeBasicSM(owner);
return;
}
// Remove nexts when not in the list of elements
for (AvatarStateMachineElement asme : getListOfElements()) {
ArrayList<AvatarStateMachineElement> removedNext = new ArrayList<>();
for (AvatarStateMachineElement nextElt : asme.getNexts()) {
if (!(getListOfElements().contains(nextElt))) {
TraceManager.addDev("Removing: " + nextElt);
removedNext.add(nextElt);
}
}
asme.getNexts().removeAll(removedNext);
}
// We check that all elements are reachable from start.
HashSet<AvatarStateMachineElement> reachable = new HashSet<>();
ArrayList<AvatarStateMachineElement> pending = new ArrayList<>();
pending.add(startState);
while (pending.size() > 0) {
AvatarStateMachineElement current = pending.get(0);
reachable.add(current);
pending.remove(0);
if (current.getNexts().size() == 0) {
if (!((current instanceof AvatarStopState) || (current instanceof AvatarState))) {
// We need to add a next
if (current instanceof AvatarTransition) {
AvatarStopState stopS = new AvatarStopState("StopState", current.getReferenceObject(), owner);
addElement(stopS);
current.addNext(stopS);
} else {
AvatarTransition at = new AvatarTransition(owner, "Transition", current.getReferenceObject());
AvatarStopState stopS = new AvatarStopState("StopState", current.getReferenceObject(), owner);
addElement(at);
addElement(stopS);
current.addNext(at);
at.addNext(stopS);
}
}
}
for (AvatarStateMachineElement nextElt : current.getNexts()) {
if (!(reachable.contains(nextElt))) {
pending.add(nextElt);
}
}
}
// We remove all elements that are not reachable
ArrayList<AvatarElement> toRemove = new ArrayList<>();
for (AvatarStateMachineElement asme : getListOfElements()) {
if (!(reachable.contains(asme))) {
TraceManager.addDev("Not reachable: " + asme);
toRemove.add(asme);
}
}
elements.removeAll(toRemove);
}
public void addElement(AvatarStateMachineElement _element) {
if (_element != null) {
elements.add(_element);
//TraceManager.addDev("Adding element " + _element);
states = null;
} else {
TraceManager.addDev("NULL element found " + _element);
}
}
public void removeElement(AvatarStateMachineElement _element) {
elements.remove(_element);
states = null;
}
public List<AvatarStateMachineElement> getListOfElements() {
return elements;
}
private void makeStates() {
states = new LinkedList<AvatarStateMachineElement>();
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarState) {
states.add(asme);
}
}
}
public AvatarState getStateByName(String _name) {
for (AvatarElement ae : elements) {
if (ae instanceof AvatarState) {
if (ae.getName().compareTo(_name) == 0) {
return (AvatarState) ae;
}
}
}
return null;
}
public void makeAllStates() {
int cpt = 0;
allStates = new AvatarStateElement[getNbOfStatesElement()];
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarStateElement) {
allStates[cpt] = (AvatarStateElement) asme;
cpt++;
}
}
}
public boolean hasTimerOperatorWithAttribute(AvatarAttribute aa) {
for(AvatarStateMachineElement asme: elements) {
if (asme instanceof AvatarTimerOperator) {
TraceManager.addDev("Found timer operator with name=" + ((AvatarTimerOperator)(asme)).getTimer().getName() +
" comparing with " + aa.getName());
if ( ((AvatarTimerOperator)(asme)).getTimer().getName().compareTo(aa.getName()) == 0) {
return true;
}
}
}
return false;
}
public int stateNb() {
if (states == null) {
makeStates();
}
return states.size();
}
public int getNbOfASMGraphicalElements() {
int cpt = 0;
for (AvatarElement elt : elements) {
if (elt.getReferenceObject() != null) {
cpt++;
}
}
return cpt;
}
public AvatarState getState(int index) {
if (states == null) {
makeStates();
}
try {
return (AvatarState) (states.get(index));
} catch (Exception e) {
}
return null;
}
public boolean isSignalUsed(AvatarSignal _sig) {
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarActionOnSignal) {
AvatarActionOnSignal aaos = (AvatarActionOnSignal) asme;
if (aaos.getSignal() == _sig) {
return true;
}
}
}
return false;
}
public boolean isTimerUsed(AvatarAttribute _timer) {
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarTimerOperator) {
AvatarTimerOperator ato = (AvatarTimerOperator) asme;
if (ato.getTimer() == _timer) {
return true;
}
}
}
return false;
}
/**
* Analyze the usage of a regular attribute (int, bool)
*
* @param _aa
* @return
*/
public boolean isRegularAttributeUsed(AvatarAttribute _aa) {
boolean ret;
AvatarIBSolver.clearAttributes();
for (AvatarStateMachineElement asme : elements) {
if (asme instanceof AvatarTransition) {
// Must check the guard, the delays and all the actions
AvatarTransition at = (AvatarTransition) asme;
if (at.isGuarded()) {
ret = isInExpression(at.getGuard().toString(), _aa);
if (ret) {
return true;
}
}
if (at.hasDelay()) {
ret = isInExpression(at.getMinDelay().toString(), _aa);
if (ret) {
return true;
}
ret = isInExpression(at.getMaxDelay().toString(), _aa);
if (ret) {
return true;
}
}
for (AvatarAction act : at.getActions()) {
ret = isInExpression(act.toString(), _aa);
if (ret) {
return true;
}
}
} else if (asme instanceof AvatarActionOnSignal) {
for (String s : ((AvatarActionOnSignal) asme).getValues()) {
ret = isInExpression(s, _aa);
if (ret) {
return true;
}
}
} else if (asme instanceof AvatarRandom) {
AvatarRandom ar = (AvatarRandom) asme;
String s = ar.getVariable();
ret = isInExpression(s, _aa);
if (ret) {
return true;
}
s = ar.getMinValue();
ret = isInExpression(s, _aa);
if (ret) {
return true;
}
s = ar.getMaxValue();
ret = isInExpression(s, _aa);
if (ret) {
return true;
}
}
}
return false;
}
private boolean isInExpression(String expr, AvatarAttribute _aa) {
return AvatarIBSolver.parser.indexOfVariable(expr, _aa.getName()) > -1;
}
private int getSimplifiedElementsAux(LinkedHashMap<AvatarStateMachineElement, Integer> simplifiedElements, Set<AvatarStateMachineElement> visited,
AvatarStateMachineElement root, int counter) {
if (visited.contains(root)) {
Integer name = simplifiedElements.get(root);
if (name == null) {
if (root == this.startState)
simplifiedElements.put(root, Integer.valueOf(0));
else {
counter++;
simplifiedElements.put(root, Integer.valueOf(counter));
}
}
} else {
visited.add(root);
for (AvatarStateMachineElement asme : root.nexts)
counter = this.getSimplifiedElementsAux(simplifiedElements, visited, asme, counter);
}
return counter;
}
public LinkedHashMap<AvatarStateMachineElement, Integer> getSimplifiedElements() {
LinkedHashMap<AvatarStateMachineElement, Integer> simplifiedElements = new LinkedHashMap<AvatarStateMachineElement, Integer>();
this.getSimplifiedElementsAux(simplifiedElements, new HashSet<AvatarStateMachineElement>(), startState, 0);
return simplifiedElements;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("State machine Id=" + getID() + " tags=" + tagsToString() + "\n");
for (AvatarStateMachineElement element : elements) {
sb.append("\t" + element.getClass() + "->" + element.toString() + " tags=" + tagsToString() + "\n");
}
return sb.toString();
}
public String toStringRecursive() {
AvatarStartState ass = getStartState();
if (ass == null) {
return "empty asm";
}
return toStringRecursiveElt(ass);
}
public String toTextFormat() {
AvatarStartState ass = getStartState();
if (ass == null) {
return "Empty state machine";
}
return toTextFormatElt(ass);
}
public String toStringRecursiveElt(AvatarStateMachineElement _asme) {
//TraceManager.addDev("Handling element:" + _asme.toString());
String ret = "";
ret += "* " + _asme.toStringExtendedID() + " tags=" + tagsToString() + "\n";
for (AvatarStateMachineElement asme : _asme.getNexts()) {
ret += "\tnext: " + asme.toStringExtendedID() + " tags=" + tagsToString() + "\n";
}
for (AvatarStateMachineElement asme : _asme.getNexts()) {
ret += toStringRecursiveElt(asme);
}
return ret;
}
public String toTextFormatElt(AvatarStateMachineElement _asme) {
String ret = "";
ret += "* " + _asme.toStringExtendedID() + "\n";
for (AvatarStateMachineElement asme : _asme.getNexts()) {
ret += "\tnext: " + asme.toStringExtendedID() + "\n";
}
for (AvatarStateMachineElement asme : _asme.getNexts()) {
ret += toTextFormatElt(asme);
}
return ret;
}
// Add missing implicit states.
public void makeFullStates(AvatarBlock _block) {
addStatesToEmptyNonTerminalEmptyNext(_block);
addStateAfterActionOnSignal(_block);
addStatesToTransitionsBetweenTwoNonStates(_block);
addStatesToActionTransitions(_block);
addStatesToNonEmptyTransitionsBetweenNonStateToState(_block);
}
private void addStatesToEmptyNonTerminalEmptyNext(AvatarBlock _b) {
List<AvatarStateMachineElement> toConsider = new ArrayList<AvatarStateMachineElement>();
for (AvatarStateMachineElement elt : elements) {
if (!(elt instanceof AvatarStopState)) {
if (elt.getNext(0) == null) {
// Missing state
toConsider.add(elt);
}
}
}
for (AvatarStateMachineElement elt : toConsider) {
AvatarStopState stopMe = new AvatarStopState("stopCreated", elt.getReferenceObject(), _b);
addElement(stopMe);
AvatarTransition tr = new AvatarTransition(_b, "trForStopCreated", elt.getReferenceObject());
addElement(tr);
elt.addNext(tr);
tr.addNext(stopMe);
}
}
private void addStateAfterActionOnSignal(AvatarBlock _block) {
List<AvatarStateMachineElement> toAdd = new ArrayList<AvatarStateMachineElement>();
int id = 0;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarActionOnSignal) {
if (elt.getNext(0) instanceof AvatarTransition) {
AvatarTransition tr = (AvatarTransition) elt.getNext(0);
// We create an intermediate state
//AvatarState state = new AvatarState("IntermediateState4__" + id, elt.getReferenceObject(), _block);
AvatarState state = new AvatarState("IntermediateState4__" + id, null, _block);
state.setCommit(true);
toAdd.add(state);
//AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState4__" + id, elt.getReferenceObject());
AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState4__" + id, null);
toAdd.add(at1);
elt.removeAllNexts();
elt.addNext(at1);
at1.addNext(state);
state.addNext(tr);
tr.setReferenceObject(null);
if (tr.getOtherReferenceObjects() != null) {
tr.getOtherReferenceObjects().clear();
}
id++;
}
}
}
for (AvatarStateMachineElement add : toAdd) {
elements.add(add);
}
}
private void addStatesToNonEmptyTransitionsBetweenNonStateToState(AvatarBlock _block) {
AvatarStateMachineElement next;
AvatarStateMachineElement previous;
List<AvatarStateMachineElement> toAdd = new ArrayList<AvatarStateMachineElement>();
int id = 0;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarTransition tr = (AvatarTransition) elt;
if (tr.hasDelay() || tr.isGuarded() || tr.hasAction()) {
previous = getPreviousElementOf(elt);
next = elt.getNext(0);
// If the next is a state, but not the previous one
if ((previous != null) && (next != null)) {
if ((!(previous instanceof AvatarStateElement)) && (next instanceof AvatarStateElement)) {
// We create an intermediate state
AvatarState state = new AvatarState("IntermediateState1__" + id, elt.getReferenceObject(), _block);
state.setCommit(true);
toAdd.add(state);
AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState1__" + id, elt.getReferenceObject());
toAdd.add(at1);
previous.removeAllNexts();
previous.addNext(at1);
at1.addNext(state);
state.addNext(tr);
tr.setReferenceObject(null);
if (tr.getOtherReferenceObjects() != null) {
tr.getOtherReferenceObjects().clear();
}
id++;
}
}
}
}
}
for (AvatarStateMachineElement add : toAdd) {
elements.add(add);
}
}
private void addStatesToTransitionsBetweenTwoNonStates(AvatarBlock _block) {
AvatarStateMachineElement next;
AvatarStateMachineElement previous;
List<AvatarStateMachineElement> toAdd = new ArrayList<AvatarStateMachineElement>();
int id = 0;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarTransition tr = (AvatarTransition) elt;
previous = getPreviousElementOf(elt);
next = elt.getNext(0);
// If the next and previous are non states
if ((previous != null) && (next != null)) {
if ((!(previous instanceof AvatarStateElement)) && (!(next instanceof AvatarStateElement))) {
// We create an intermediate state
AvatarState state = new AvatarState("IntermediateState2__" + id, elt.getReferenceObject(), _block);
toAdd.add(state);
AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState2__" + id, elt.getReferenceObject());
toAdd.add(at1);
previous.removeAllNexts();
previous.addNext(at1);
at1.addNext(state);
state.addNext(tr);
tr.setReferenceObject(null);
if (tr.getOtherReferenceObjects() != null) {
tr.getOtherReferenceObjects().clear();
}
id++;
}
}
}
}
for (AvatarStateMachineElement add : toAdd) {
elements.add(add);
}
}
// Hanlding transitions with actions which have a non state
// after
// Then, handling transitions with actions which have a non state
// before
private void addStatesToActionTransitions(AvatarBlock _block) {
AvatarStateMachineElement next;
AvatarStateMachineElement previous;
List<AvatarStateMachineElement> toAdd = new ArrayList<AvatarStateMachineElement>();
int id = 0;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarTransition tr = (AvatarTransition) elt;
// tr with actions?
if (tr.getNbOfAction() > 0) {
previous = getPreviousElementOf(elt);
next = elt.getNext(0);
if (!(next instanceof AvatarState)) {
// We create an intermediate state
AvatarState state = new AvatarState("IntermediateState3__" + id, elt.getReferenceObject(), _block);
state.setCommit(true);
toAdd.add(state);
AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState3__" + id, elt.getReferenceObject());
toAdd.add(at1);
tr.removeAllNexts();
tr.addNext(state);
tr.setReferenceObject(null);
if (tr.getOtherReferenceObjects() != null) {
tr.getOtherReferenceObjects().clear();
}
state.addNext(at1);
at1.addNext(next);
id++;
}
}
}
}
for (AvatarStateMachineElement add : toAdd) {
elements.add(add);
}
toAdd.clear();
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarTransition tr = (AvatarTransition) elt;
// tr with actions?
if (tr.getNbOfAction() > 0) {
previous = getPreviousElementOf(elt);
if (!(previous instanceof AvatarStateElement)) {
// We create an intermediate state
AvatarState state = new AvatarState("IntermediateState__" + id, elt.getReferenceObject(), _block);
state.setCommit(true);
toAdd.add(state);
AvatarTransition at1 = new AvatarTransition(_block, "TransitionForIntermediateState__" + id, elt.getReferenceObject());
toAdd.add(at1);
previous.removeAllNexts();
previous.addNext(at1);
at1.addNext(state);
state.addNext(tr);
if (tr.getOtherReferenceObjects() != null) {
tr.setReferenceObject(null);
}
tr.getOtherReferenceObjects().clear();
id++;
}
}
}
}
for (AvatarStateMachineElement add : toAdd) {
elements.add(add);
}
}
public void removeRandoms(AvatarBlock _block) {
int id = 0;
List<AvatarStateMachineElement> toRemove = new ArrayList<AvatarStateMachineElement>();
List<AvatarStateMachineElement> toAdd = new ArrayList<AvatarStateMachineElement>();
AvatarStateMachineElement next;
AvatarStateMachineElement previous;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarRandom) {
AvatarRandom random = (AvatarRandom) elt;
previous = getPreviousElementOf(elt);
next = elt.getNext(0);
toRemove.add(elt);
// Is the variable of random used in expression?
// If yes, they must be removed. If not, we can directly use the expressions.
String minExpr = random.getMinValue();
String maxExpr = random.getMaxValue();
String var = random.getVariable();
String maxExprToBeUsed = "";
// Creating elements
AvatarTransition at1 = new AvatarTransition(_block, "Transition1ForRandom__ " + elt.getName() + "__" + id, elt.getReferenceObject());
// If maxExpr contains var then use __tmpMax instead of var in maxExpr
if (AvatarIBSolver.parser.indexOfVariable(maxExpr, var) > -1) {
AvatarAttribute aa = _block.addIntegerAttribute("tmpMaxRandom__");
maxExprToBeUsed = aa.getName();
at1.addAction(aa.getName() + " = " + maxExpr, "");
} else {
maxExprToBeUsed = maxExpr;
}
at1.addAction(random.getVariable() + "=" + minExpr, "");
AvatarState randomState = new AvatarState("StateForRandom__" + elt.getName() + "__" + id, elt.getReferenceObject(), _block);
AvatarState beforeRandom = new AvatarState("StateBeforeRandom__" + elt.getName() + "__" + id, elt.getReferenceObject(), _block);
AvatarTransition at2 = new AvatarTransition(_block, "Transition2ForRandom__" + elt.getName() + "__" + id, elt.getReferenceObject());
at2.setGuard("[" + random.getVariable() + " < " + maxExprToBeUsed + "]", "");
at2.addAction(random.getVariable() + "=" + random.getVariable() + " + 1", "");
AvatarTransition at3 = new AvatarTransition(_block, "Transition3ForRandom__ " + elt.getName() + "__" + id, elt.getReferenceObject());
AvatarState afterRandom = new AvatarState("StateAfterRandom__" + elt.getName() + "__" + id, elt.getReferenceObject(), _block);
// Adding elements
toAdd.add(at1);
toAdd.add(randomState);
toAdd.add(beforeRandom);
toAdd.add(at2);
toAdd.add(at3);
toAdd.add(afterRandom);
// Linking elements
if (previous != null) {
previous.removeAllNexts();
previous.addNext(beforeRandom);
}
beforeRandom.addNext(at1);
at1.addNext(randomState);
randomState.addNext(at2);
randomState.addNext(at3);
at2.addNext(randomState);
at3.addNext(afterRandom);
afterRandom.addNext(next);
id++;
}
}
for (AvatarStateMachineElement trash : toRemove) {
elements.remove(trash);
}
for (AvatarStateMachineElement newOnes : toAdd) {
elements.add(newOnes);
}
}
public AvatarStartState getStartStateOf(AvatarStateMachineElement source) {
for(AvatarStateMachineElement asme: elements) {
//TraceManager.addDev("getStartStateOf(): investigating element " + asme);
if (asme.getState() != null) {
//TraceManager.addDev("Found element " + asme + " which state is " + source);
}
if ((asme instanceof AvatarStartState) && (source == asme.getState())) {
return (AvatarStartState) asme;
}
}
return null;
}
// Assumes no after clause on composite relation
public void removeCompositeStates(AvatarBlock _block) {
//TraceManager.addDev("\n-------------- Remove composite states ---------------\n");
/*LinkedList<AvatarState> lists =*/
//List<AvatarStartState> compositeStateToBeRemoved = removeAllInternalStartStates();
removeAllInternalStopStatesByRegularStates();
AvatarTransition at = getAvatarCompositeTransition();
if (at == null) {
return;
}
// We modify all composite states with intermediate states
for (int i = 0; i < elements.size(); i++) {
AvatarStateMachineElement element = elements.get(i);
if (element instanceof AvatarState) {
modifyStateForCompositeSupport((AvatarState) element);
}
}
// For each composite transition: We link it to all the subStates of the current state
AvatarState src;
Vector<AvatarTransition> transitions = getAvatarCompositeTransitions();
for (AvatarTransition atc : transitions) {
src = (AvatarState) (getPreviousElementOf(atc));
atc.setAsVerifiable(false);
for (int j = 0; j < elements.size(); j++) {
AvatarStateMachineElement elt = elements.get(j);
if ((elt instanceof AvatarState) && (elt.hasInUpperState(src))) {
AvatarTransition att = cloneCompositeTransition(atc);
elt.addNext(att);
att.setAsVerifiable(false);
}
}
}
/*while (((at = getAvatarCompositeTransition()) != null)) {
src = (AvatarState) (getPreviousElementOf(at));
elements.remove(at);
// Link a clone of the transition to all internal states
for (int j = 0; j < elements.size(); j++) {
AvatarStateMachineElement elt = elements.get(j);
if ((elt instanceof AvatarState) && (elt.hasInUpperState(src))) {
AvatarTransition att = cloneCompositeTransition(at);
elt.addNext(att);
att.setAsVerifiable(false);
at.setAsVerifiable(false);
}
}
}*/
// Removing composite states
/*for(AvatarStateMachineElement elt: compositeStateToBeRemoved) {
elements.remove(elt);
}*/
}
private void removeAllInternalStopStatesByRegularStates() {
Vector<AvatarStopState> v = new Vector<>();
Vector<AvatarState> toAdd = new Vector<>();
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) element;
//TraceManager.addDev("at? element=" + element);
// Transition fully in the internal state?
if (element.getNext(0) instanceof AvatarStopState) {
AvatarStopState stop = (AvatarStopState) (element.getNext(0));
if (stop.getState() != null) {
AvatarState newState = new AvatarState(stop.getName(), stop.getReferenceObject(), block);
element.removeNext(0);
element.addNext(newState);
toAdd.add(newState);
v.add(stop);
}
}
}
}
for (AvatarStopState s : v) {
elements.remove(s);
}
for (AvatarState st : toAdd) {
elements.add(st);
}
}
private void modifyStateForCompositeSupport(AvatarState _state) {
// Each time there is a transition with an after or more than one action, we must rework the transition
// We first gather all transitions internal to that state
Vector<AvatarStateMachineElement> v = new Vector<AvatarStateMachineElement>();
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) element;
//TraceManager.addDev("at? element=" + element);
// Transition fully in the internal state?
if (element.getNext(0).hasInUpperState(_state) == true) {
AvatarStateMachineElement previous = getPreviousElementOf(element);
if (previous.hasInUpperState(_state) == true) {
if (!(at.isEmpty())) {
v.add(at);
at.setNotCheckable();
}
}
}
}
}
for (AvatarStateMachineElement element : v) {
//TraceManager.addDev(">" + element + "<");
splitAvatarTransition((AvatarTransition) element, _state);
}
}
private void splitAvatarTransition(AvatarTransition _at, AvatarState _currentState) {
if (_at.hasDelay()) {
AvatarStateMachineElement element = getPreviousElementOf(_at);
if (element.hasInUpperState(_currentState) == true) {
if (!(element instanceof AvatarState)) {
// Must add an intermediate state
String tmp = findUniqueStateName("splitstate_after__");
AvatarState as = new AvatarState(tmp, _currentState.getReferenceObject(), block);
addElement(as);
as.setHidden(true);
as.setState(_currentState);
AvatarTransition atn = new AvatarTransition(_at.getBlock(), "splittransition_after", null);
addElement(atn);
element.removeNext(_at);
element.addNext(atn);
atn.addNext(as);
as.addNext(_at);
splitAvatarTransition(_at, _currentState);
}
}
} else {
if (_at.getNbOfAction() > 1) {
//TraceManager.addDev("New split state");
String tmp = findUniqueStateName("splitstate_action__");
AvatarState as = new AvatarState(tmp, null, block);
as.setHidden(true);
as.setState(_currentState);
AvatarTransition at = (AvatarTransition) (_at.basicCloneMe(block));
_at.removeAllActionsButTheFirstOne();
at.removeFirstAction();
at.addNext(_at.getNext(0));
_at.removeAllNexts();
_at.addNext(as);
as.addNext(at);
addElement(as);
addElement(at);
splitAvatarTransition(_at, _currentState);
}
}
}
//
// private List<AvatarTransition> getAllAvatarCompositeTransitions() {
// List<AvatarTransition> ats = new ArrayList<AvatarTransition>();
// for (AvatarStateMachineElement element : elements) {
// if (element instanceof AvatarTransition) {
// if ((isACompositeTransition((AvatarTransition) element))) {
// ats.add((AvatarTransition) element);
// }
// }
// }
//
// return ats;
// }
private void addFullInternalStates(AvatarState state, AvatarTransition _at) {
// First:split internal transitions
Vector<AvatarStateMachineElement> v = new Vector<AvatarStateMachineElement>();
for (AvatarStateMachineElement element : elements) {
//TraceManager.addDev("\nIs in composite state " + state + ": >" + element + "< ???");
if (element instanceof AvatarTransition) {
//TraceManager.addDev("at? element=" + element);
if (element.getNext(0).hasInUpperState(state) == true) {
if (getPreviousElementOf(element).hasInUpperState(state) == true) {
v.add(element);
}
}
} else if (element.hasInUpperState(state) == true) {
// We found a candidate!
if (element != _at) {
v.add(element);
}
}
}
//TraceManager.addDev("*** Analyzing components in state " + state);
// Split avatar transitions
for (AvatarStateMachineElement element : v) {
//TraceManager.addDev(">" + element + "<");
if (element instanceof AvatarTransition) {
splitAvatarTransition((AvatarTransition) element, state);
}
}
}
private AvatarTransition getAvatarCompositeTransition() {
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
if ((isACompositeTransition((AvatarTransition) element))) {
return (AvatarTransition) element;
}
}
}
return null;
}
private Vector<AvatarTransition> getAvatarCompositeTransitions() {
Vector<AvatarTransition> transitions = new Vector<>();
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
if ((isACompositeTransition((AvatarTransition) element))) {
transitions.add((AvatarTransition) element);
}
}
}
return transitions;
}
// Checks whether the previous element is a state with an internal state machine
public boolean isACompositeTransition(AvatarTransition _at) {
AvatarStateMachineElement element = getPreviousElementOf(_at);
if (element == null) {
return false;
}
if (!(element instanceof AvatarState)) {
return false;
}
AvatarState state = (AvatarState) element;
if (!hasInternalComponents(state)) {
return false;
}
AvatarStateMachineElement next = _at.getNext(0);
if (next.hasInUpperState(state)) {
return false;
}
return true;
}
private boolean hasInternalComponents(AvatarState _state) {
for (AvatarStateMachineElement element : elements) {
if (element.getState() == _state) {
return true;
}
}
return false;
}
/*private void removeCompositeTransition(AvatarTransition _at, AvatarBlock _block) {
AvatarState state = (AvatarState)(getPreviousElementOf(_at));
removeStopStatesOf(state);
// Remove "after" and replace them with timers
AvatarTransition at = removeAfter(_at, _block);
// Put state after transition
modifyAvatarTransition(at);
Vector <AvatarStateMachineElement> v = new Vector<AvatarStateMachineElement>();
for(AvatarStateMachineElement element: elements) {
//TraceManager.addDev("\nIs in composite state " + state + ": >" + element + "< ???");
if (element instanceof AvatarTransition) {
//TraceManager.addDev("at? element=" + element);
if (element.getNext(0).hasInUpperState(state) == true) {
if (getPreviousElementOf(element).hasInUpperState(state) == true) {
v.add(element);
}
}
} else if (element.hasInUpperState(state) == true) {
// We found a candidate!
if (element != _at) {
v.add(element);
}
}
}
//TraceManager.addDev("*** Analyzing components in state " + state);
// Split avatar transitions
for(AvatarStateMachineElement element: v) {
TraceManager.addDev(">" + element + "<");
if (element instanceof AvatarTransition) {
splitAvatarTransition((AvatarTransition)element, state);
}
}
//TraceManager.addDev("\nAdding new elements in state");
v.clear();
for(AvatarStateMachineElement element: elements) {
//TraceManager.addDev("\nIs in composite state " + state + ": >" + element + "< ???");
if (element.hasInUpperState(state) == true) {
// We found a candidate!
if ((element != _at) && (element != at)) {
v.add(element);
}
}
}
for(AvatarStateMachineElement element: v) {
adaptCompositeTransition(at, element, 0);
}
removeElement(at);
}*/
/*private void removeCompositeTransitions(ArrayList<AvatarTransition> _ats, AvatarBlock _block) {
// Put state after transition
modifyAvatarTransition(at);
Vector <AvatarStateMachineElement> v = new Vector<AvatarStateMachineElement>();
for(AvatarStateMachineElement element: elements) {
//TraceManager.addDev("\nIs in composite state " + state + ": >" + element + "< ???");
if (element instanceof AvatarTransition) {
//TraceManager.addDev("at? element=" + element);
if (element.getNext(0).hasInUpperState(state) == true) {
if (getPreviousElementOf(element).hasInUpperState(state) == true) {
v.add(element);
}
}
} else if (element.hasInUpperState(state) == true) {
// We found a candidate!
if (element != _at) {
v.add(element);
}
}
}
//TraceManager.addDev("*** Analyzing components in state " + state);
// Split avatar transitions
for(AvatarStateMachineElement element: v) {
TraceManager.addDev(">" + element + "<");
if (element instanceof AvatarTransition) {
splitAvatarTransition((AvatarTransition)element, state);
}
}
//TraceManager.addDev("\nAdding new elements in state");
v.clear();
for(AvatarStateMachineElement element: elements) {
//TraceManager.addDev("\nIs in composite state " + state + ": >" + element + "< ???");
if (element.hasInUpperState(state) == true) {
// We found a candidate!
if ((element != _at) && (element != at)) {
v.add(element);
}
}
}
for(AvatarStateMachineElement element: v) {
adaptCompositeTransition(at, element, 0);
}
removeElement(at);
}*/
// private void splitAvatarTransitionOld(AvatarTransition _at, AvatarState _currentState) {
// /*if (_at.hasCompute()) {
// AvatarState as0 = new AvatarState("splitstate0", null);
// AvatarState as1 = new AvatarState("splitstate1", null);
//
//
//
// AvatarTransition at = _at.basicCloneMe();
// _at.removeAllActions();
// _at.removeAllNexts();
// _at.addNext(as);
// as.addNext(at);
// addElement(as);
// addElement(at);
// splitAvatarTransition(at);
// }*/
//
// TraceManager.addDev(" - - - - - - - - - - Split transition nbofactions=" + _at.getNbOfAction());
// if (_at.getNbOfAction() > 1) {
// TraceManager.addDev("New split state");
// AvatarState as = new AvatarState("splitstate", null);
// as.setHidden(true);
// as.setState(_currentState);
// AvatarTransition at = (AvatarTransition) (_at.basicCloneMe(block));
// _at.removeAllActionsButTheFirstOne();
// at.removeFirstAction();
// at.addNext(_at.getNext(0));
// _at.removeAllNexts();
// _at.addNext(as);
// as.addNext(at);
// addElement(as);
// addElement(at);
//
// splitAvatarTransition(_at, _currentState);
// }
//
// if (_at.hasDelay()) {
// AvatarStateMachineElement element = getPreviousElementOf(_at);
// if (element.hasInUpperState(_currentState) == true) {
// if (!(element instanceof AvatarState)) {
// // Must add an intermediate state
// String tmp = findUniqueStateName("internalstate__");
// AvatarState as = new AvatarState(tmp, _currentState.getReferenceObject());
// addElement(as);
// as.setHidden(true);
// as.setState(_currentState);
// AvatarTransition atn = new AvatarTransition(_at.getBlock(), "internaltransition", null);
// addElement(atn);
// element.removeNext(_at);
// element.addNext(atn);
// atn.addNext(as);
// as.addNext(_at);
// splitAvatarTransition(_at, _currentState);
// }
// }
// }
// }
// private void adaptCompositeTransition(AvatarTransition _at, AvatarStateMachineElement _element, int _transitionID) {
// AvatarState as;
// AvatarTransition at;
// LinkedList<AvatarStateMachineElement> ll;
// String tmp;
//
// // It cannot be a start / stop state since they have been previously removed ..
// if (_element instanceof AvatarActionOnSignal) {
// AvatarStateMachineElement element = _element.getNext(0);
// if (element instanceof AvatarTransition) {
// if (!(((AvatarTransition) element).isEmpty())) {
// //We need to create a new state
// tmp = findUniqueStateName("internalstate__");
// TraceManager.addDev("Creating state with name=" + tmp);
// as = new AvatarState(tmp, null);
// addElement(as);
// as.setHidden(true);
// at = new AvatarTransition(_at.getBlock(), "internaltransition", null);
// addElement(at);
// //_element -> at -> as -> element
//
// _element.removeNext(element);
// _element.addNext(at);
//
// at.addNext(as);
// as.addNext(element);
//
// at = cloneCompositeTransition(_at);
// //addElement(at);
// as.addNext(at);
// } else {
// // We see if a state follows it. Otherwise, we create one
// if (!(element.getNext(0) instanceof AvatarState)) {
// //We need to create a new state
// tmp = findUniqueStateName("internalstate__");
// TraceManager.addDev("Creating state with name=" + tmp);
// as = new AvatarState(tmp, null);
// addElement(as);
// as.setHidden(true);
// at = new AvatarTransition(_at.getBlock(), "internaltransition", null);
// addElement(at);
// //_element -> at -> as -> element
//
// _element.removeNext(element);
// _element.addNext(at);
//
// at.addNext(as);
// as.addNext(element);
//
// at = cloneCompositeTransition(_at);
// //addElement(at);
// as.addNext(at);
//
// } else {
// //We link to this state-> will be done later
// }
// }
// }
// /*ll = getPreviousElementsOf(_element);
// for(AvatarStateMachineElement element: ll) {
// if (element instanceof AvatarTransition) {
// // if empty transition: we do just nothing
// if (!(((AvatarTransition)element).isEmpty())) {
// tmp = findUniqueStateName("internalstate__");
// TraceManager.addDev("Creating state with name=" + tmp);
// as = new AvatarState(tmp, null);
// as.setHidden(true);
// element.removeNext(_element);
// element.addNext(as);
// at = new AvatarTransition("internaltransition", null);
// addElement(at);
// at.addNext(_element);
// as.addNext(at);
// addElement(as);
//
// at = cloneCompositeTransition(_at);
// addElement(at);
// as.addNext(at);
// }
//
// } else {
// // Badly formed machine!
// TraceManager.addError("Badly formed sm (removing composite transition)");
// }
// }*/
//
// } else if (_element instanceof AvatarState) {
// at = cloneCompositeTransition(_at);
// //addElement(at);
// _element.addNext(at);
// } else if (_element instanceof AvatarTransition) {
// // Nothing to do since they shall have been split before
// } else {
// // Nothing to do either
// }
// }
// Return the first previous element met. Shall be used preferably only for transitions
private AvatarStateMachineElement getPreviousElementOf(AvatarStateMachineElement _elt) {
for (AvatarStateMachineElement element : elements) {
if (element.hasNext(_elt)) {
return element;
}
}
return null;
}
private List<AvatarStateMachineElement> getPreviousElementsOf(AvatarStateMachineElement _elt) {
List<AvatarStateMachineElement> ll = new LinkedList<AvatarStateMachineElement>();
for (AvatarStateMachineElement element : elements) {
if (element.hasNext(_elt)) {
ll.add(element);
}
}
return ll;
}
public AvatarState getStateWithName(String _name) {
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarState) {
if (element.getName().compareTo(_name) == 0) {
return (AvatarState) element;
}
}
}
return null;
}
public List<AvatarActionOnSignal> getAllAOSWithName(String _name) {
List<AvatarActionOnSignal> list = new ArrayList<AvatarActionOnSignal>();
for (AvatarStateMachineElement element : elements) {
if (element instanceof AvatarActionOnSignal) {
AvatarActionOnSignal aaos = (AvatarActionOnSignal) element;
if (aaos.getSignal().getName().compareTo(_name) == 0) {
list.add(aaos);
}
}
}
return list;
}
// All transitions reaching a state that has an internal start state
// shall in fact go directly to the nexts of the start state
public List<AvatarStartState> removeAllInternalStartStates() {
// identify allstart state
List<AvatarStartState> ll = new LinkedList<AvatarStartState>();
List<AvatarStartState> removedinfos = new LinkedList<>();
for (AvatarStateMachineElement element : elements) {
if ((element instanceof AvatarStartState) && (element.getState() != null)) {
//TraceManager.addDev("-> -> found an internal state state");
ll.add((AvatarStartState) element);
}
}
AvatarState as0;
List<AvatarStateMachineElement> le;
for (AvatarStartState as : ll) {
AvatarState astate = as.getState();
if (as != null) {
elements.remove(as);
AvatarStateMachineElement elt = as.getNext(0);
astate.addNext(elt);
removedinfos.add(as);
/*le = getPreviousElementsOf(astate);
if (le.size() > 0) {
as0 = new AvatarState("entrance__" + astate.getName(), astate.getReferenceObject());
as0.addNext(as.getNext(0));
as0.setHidden(true);
as0.setState(astate);
for (AvatarStateMachineElement element : le) {
if (element instanceof AvatarTransition) {
element.removeAllNexts();
element.addNext(as0);
} else {
TraceManager.addDev("Badly formed state machine");
}
}
removedinfos.add(as.getState());
removedinfos.add(as0);
// Remove the start state and its next transition
removeElement(as);
addElement(as0);
//TraceManager.addDev("-> -> removed an internal state state!");
} else {
TraceManager.addDev("Badly formed state machine");
}*/
}
}
return removedinfos;
}
public void removeAllSuperStates() {
for (AvatarStateMachineElement element : elements) {
element.setState(null);
}
}
public AvatarStateMachineElement getStateMachineElementFromReferenceObject(Object _o) {
for (AvatarStateMachineElement element : elements) {
if (element.hasReferenceObject(_o)) {
return element;
}
}
return null;
}
public Object getReferenceObjectFromID(int _ID) {
for (AvatarStateMachineElement element : elements) {
if (element.getID() == _ID) {
return element.getReferenceObject();
}
}
return null;
}
// Return true iff at least one timer was removed
public boolean removeTimers(String timerAttributeName, HashMap<String, AvatarSignal> sets,
HashMap<String, AvatarSignal> resets, HashMap<String, AvatarSignal> expires) {
AvatarSetTimer ast;
AvatarTimerOperator ato;
List<AvatarStateMachineElement> olds = new LinkedList<AvatarStateMachineElement>();
List<AvatarStateMachineElement> news = new LinkedList<AvatarStateMachineElement>();
AvatarSignal as;
for (AvatarStateMachineElement elt : elements) {
// Set timer...
if (elt instanceof AvatarSetTimer) {
ast = (AvatarSetTimer) elt;
as = sets.get(ast.getTimer().getName());
if (as != null) {
AvatarActionOnSignal aaos = new AvatarActionOnSignal(elt.getName(), as, elt.getReferenceObject(), block);
aaos.addValue(timerAttributeName);
olds.add(elt);
news.add(aaos);
// Modifying the transition just before
List<AvatarStateMachineElement> previous = getPreviousElementsOf(ast);
if (previous.size() == 1) {
if (previous.get(0) instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) (previous.get(0));
TraceManager.addDev("Timer value setting=" + ast.getTimerValue());
at.addAction(timerAttributeName + " = " + ast.getTimerValue(), "");
} else {
TraceManager.addError("The element before a set time is not a transition!");
}
} else {
TraceManager.addError("More than one transition before a set time!");
}
}
// Reset timer
} else if (elt instanceof AvatarResetTimer) {
ato = (AvatarTimerOperator) elt;
as = resets.get(ato.getTimer().getName());
if (as != null) {
AvatarActionOnSignal aaos = new AvatarActionOnSignal(elt.getName(), as, elt.getReferenceObject(), block);
olds.add(elt);
news.add(aaos);
}
// Expire timer
} else if (elt instanceof AvatarExpireTimer) {
ato = (AvatarTimerOperator) elt;
as = expires.get(ato.getTimer().getName());
if (as != null) {
AvatarActionOnSignal aaos = new AvatarActionOnSignal(elt.getName(), as, elt.getReferenceObject(), block);
olds.add(elt);
news.add(aaos);
}
}
}
// Replacing old elements with new ones
AvatarStateMachineElement oldelt, newelt;
for (int i = 0; i < olds.size(); i++) {
oldelt = olds.get(i);
newelt = news.get(i);
replace(oldelt, newelt);
}
return (olds.size() > 0);
}
public void replace(AvatarStateMachineElement oldone, AvatarStateMachineElement newone) {
//TraceManager.addDev("Replacing " + oldone + " with " + newone);
addElement(newone);
removeElement(oldone);
// Previous elements
List<AvatarStateMachineElement> previous = getPreviousElementsOf(oldone);
for (AvatarStateMachineElement elt : previous) {
elt.replaceAllNext(oldone, newone);
}
// Next elements
for (int i = 0; i < oldone.nbOfNexts(); i++) {
AvatarStateMachineElement elt = oldone.getNext(i);
newone.addNext(elt);
}
}
public AvatarTransition removeAfter(AvatarTransition _at, AvatarBlock _block) {
String delay = _at.getMinDelay();
if ((delay == null) || (delay.trim().length() == 0)) {
return _at;
}
// We have to use a timer for this transition
AvatarAttribute aa = _block.addTimerAttribute("timer__");
AvatarAttribute val = _block.addIntegerAttribute(aa.getName() + "_val");
//TraceManager.addDev("ADDING TIMER: " + aa.getName());
// Timer is set at the entrance in the composite state
List<AvatarTransition> ll = findEntranceTransitionElements((AvatarState) (getPreviousElementOf(_at)));
AvatarTransition newat0, newat1;
AvatarSetTimer ast;
AvatarRandom ar;
AvatarState as;
for (AvatarTransition att : ll) {
//TraceManager.addDev(" ------------------ Dealing with an entrance transition");
ar = new AvatarRandom("randomfortimer", _block.getReferenceObject(), _block);
ar.setVariable(val.getName());
ar.setValues(_at.getMinDelay(), _at.getMaxDelay());
ast = new AvatarSetTimer("settimer_" + aa.getName(), _block.getReferenceObject(), _block);
ast.setTimerValue(val.getName());
ast.setTimer(aa);
newat0 = new AvatarTransition(_block, "transition_settimer_" + aa.getName(), _block.getReferenceObject());
newat1 = new AvatarTransition(_block, "transition_settimer_" + aa.getName(), _block.getReferenceObject());
elements.add(ar);
elements.add(ast);
elements.add(newat0);
elements.add(newat1);
newat1.addNext(att.getNext(0));
att.removeAllNexts();
att.addNext(ar);
ar.addNext(newat0);
newat0.addNext(ast);
ast.addNext(newat1);
}
// Wait for timer expiration on the transition
AvatarExpireTimer aet = new AvatarExpireTimer("expiretimer_" + aa.getName(), _block.getReferenceObject(), _block);
aet.setTimer(aa);
newat0 = new AvatarTransition(_block, "transition0_expiretimer_" + aa.getName(), _block.getReferenceObject());
newat1 = new AvatarTransition(_block, "transition1_expiretimer_" + aa.getName(), _block.getReferenceObject());
as = new AvatarState("state1_expiretimer_" + aa.getName(), _block.getReferenceObject(), _block);
addElement(aet);
addElement(newat0);
addElement(newat1);
addElement(as);
newat0.addNext(aet);
aet.addNext(newat1);
newat1.addNext(as);
_at.setDelays("", "", "", "");
List<AvatarStateMachineElement> elts = getElementsLeadingTo(_at);
for (AvatarStateMachineElement elt : elts) {
elt.removeNext(_at);
elt.addNext(newat0);
}
as.addNext(_at);
return newat0;
}
public List<AvatarTransition> findEntranceTransitionElements(AvatarState _state) {
//TraceManager.addDev("Searching for transitions entering:" + _state.getName());
List<AvatarTransition> ll = new LinkedList<AvatarTransition>();
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarStateMachineElement element = getPreviousElementOf(elt);
if (elt.getNext(0) == _state) {
ll.add((AvatarTransition) elt);
} else if (element.inAnUpperStateOf(_state) && (elt.getNext(0).getState() == _state)) {
ll.add((AvatarTransition) elt);
}
}
}
//TraceManager.addDev("Nb of elements found:" + ll.size());
return ll;
}
public List<AvatarStateMachineElement> getElementsLeadingTo(AvatarStateMachineElement _elt) {
List<AvatarStateMachineElement> elts = new LinkedList<AvatarStateMachineElement>();
for (AvatarStateMachineElement elt : elements) {
if (elt.hasNext(_elt)) {
elts.add(elt);
}
}
return elts;
}
public void modifyAvatarTransition(AvatarTransition _at) {
/*if ((_at.getNbOfAction() > 0) || (_at.hasCompute())) {
return;
}*/
AvatarStateMachineElement next = _at.getNext(0);
if (next instanceof AvatarState) {
return;
} else if ((next instanceof AvatarTimerOperator) || (next instanceof AvatarActionOnSignal)) {
TraceManager.addDev("-> Timer modification");
AvatarState myState = new AvatarState("statefortransition__" + ID_ELT, _at.getReferenceObject(), block);
myState.setHidden(true);
AvatarTransition at2 = new AvatarTransition(_at.getBlock(), "transitionfortransition__" + ID_ELT, _at.getReferenceObject());
ID_ELT++;
AvatarTransition at1 = (AvatarTransition) (next.getNext(0));
next.removeAllNexts();
next.addNext(at2);
at2.addNext(myState);
myState.addNext(at1);
addElement(myState);
addElement(at2);
return;
} else {
AvatarState myState = new AvatarState("statefortransition__" + ID_ELT, _at.getReferenceObject(), block);
AvatarTransition at = new AvatarTransition(_at.getBlock(), "transitionfortransition__", _at.getReferenceObject());
at.addNext(_at.getNext(0));
_at.removeAllNexts();
_at.addNext(myState);
myState.addNext(at);
addElement(myState);
addElement(at);
return;
}
}
public AvatarTransition cloneCompositeTransition(AvatarTransition _at) {
//TraceManager.addDev("Must clone: " + _at);
// We clone elements until we find a state!
AvatarStateMachineElement tomake, current;
AvatarStateMachineElement tmp;
AvatarTransition at = (AvatarTransition) (_at.basicCloneMe(block));
addElement(at);
current = _at.getNext(0);
tomake = at;
while ((current != null) && !(current instanceof AvatarState)) {
//TraceManager.addDev("Cloning: " + current);
current.setNotCheckable();
current.setAsVerifiable(false);
tmp = current.basicCloneMe(block);
addElement(tmp);
tomake.addNext(tmp);
tomake = tmp;
current = current.getNext(0);
if (current == null) {
break;
}
}
if (current == null) {
TraceManager.addDev("NULL CURRENT !!! NULL CURRENT");
}
tomake.addNext(current);
return at;
/*if ((_at.getNbOfAction() > 0) || (_at.hasCompute())) {
return _at.basicCloneMe();
}
AvatarStateMachineElement next = _at.getNext(0);
if (next instanceof AvatarActionOnSignal) {
AvatarTransition at = _at.
AvatarActionOnSignal aaos = ((AvatarActionOnSignal)next).basicCloneMe();
addElement(at);
addElement(aaos);
at.addNext(aaos);
aaos.addNext(next.getNext(0)); // Shall be a state!
return at;
}
if (next instanceof AvatarExpireTimer) {
AvatarTransition at = _at.basicCloneMe();
AvatarExpireTimer aet = ((AvatarExpireTimer)next).basicCloneMe();
AvatarTransition
addElement(at);
addElement(aet);
at.addNext(aet);
aet.addNext(next.getNext(0)); // Shall be a state!
return at;
}
if (next instanceof AvatarResetTimer) {
AvatarTransition at = _at.basicCloneMe();
AvatarResetTimer art = ((AvatarResetTimer)next).basicCloneMe();
addElement(at);
addElement(art);
at.addNext(art);
art.addNext(next.getNext(0)); // Shall be a state!
return at;
}
if (next instanceof AvatarSetTimer) {
AvatarTransition at = _at.basicCloneMe();
AvatarSetTimer ast = ((AvatarSetTimer)next).basicCloneMe();
addElement(at);
addElement(ast);
at.addNext(ast);
ast.addNext(next.getNext(0)); // Shall be a state!
return at;
}
return _at.basicCloneMe();*/
}
public void removeStopStatesOf(AvatarState _as) {
List<AvatarStopState> ll = new LinkedList<AvatarStopState>();
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarStopState) {
if (elt.getState() == _as) {
ll.add((AvatarStopState) elt);
}
}
}
for (AvatarStopState ass : ll) {
TraceManager.addDev("Removed a stop state");
AvatarState astate = new AvatarState("OldStopState", ass.getReferenceObject(), block);
astate.setState(ass.getState());
replace(ass, astate);
}
}
public String findUniqueStateName(String name) {
int id = 0;
boolean found;
while (id < 10000) {
found = false;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarState) {
if (elt.getName().compareTo(name + id) == 0) {
found = true;
break;
}
}
}
if (!found) {
return name + id;
}
id++;
}
return name + id;
}
public void handleUnfollowedStartState(AvatarStateMachineOwner _block) {
if (startState.nbOfNexts() == 0) {
AvatarStopState stopState = new AvatarStopState("__StopState", startState.getReferenceObject(), _block);
AvatarTransition at = new AvatarTransition(_block, "__toStop", startState.getReferenceObject());
addElement(stopState);
addElement(at);
startState.addNext(at);
at.addNext(stopState);
}
}
/**
* Removes all function calls by inlining them.
*
* @param block The block from which library function calls should be removed.
*/
public void removeLibraryFunctionCalls(AvatarBlock block) {
/* Perform BFS for AvatarLibraryFunctionCall elements. When one is found, replace it by the state machine and fix the links */
LinkedList<AvatarStateMachineElement> toVisit = new LinkedList<AvatarStateMachineElement>();
toVisit.add(this.startState);
Set<AvatarStateMachineElement> visited = new HashSet<AvatarStateMachineElement>();
Map<AvatarLibraryFunctionCall, AvatarStateMachineElement> callsTranslated = new HashMap<AvatarLibraryFunctionCall, AvatarStateMachineElement>();
while (!toVisit.isEmpty()) {
/* Get the first element of the queue */
AvatarStateMachineElement curAsme = toVisit.remove();
if (visited.contains(curAsme))
continue;
if (curAsme instanceof AvatarLibraryFunctionCall) {
AvatarLibraryFunctionCall alfc = (AvatarLibraryFunctionCall) curAsme;
/* Create a state that will be used as an entry point for the sub-state machine */
AvatarState firstState = new AvatarState("entry_" + alfc.getLibraryFunction().getName() + "_" + alfc.getCounter(),
curAsme.getReferenceObject(), block);
elements.add(firstState);
/* Add this state to the mapping so that future state can use it to replace their next element */
callsTranslated.put(alfc, firstState);
/* inline the function call */
AvatarStateMachineElement lastState = alfc.inlineFunctionCall(block, firstState);
TraceManager.addDev("LAST STATE IS " + lastState + " class=" + lastState.getClass().getCanonicalName());
/* Add the next elements to the newly created last state */
for (AvatarStateMachineElement asme : curAsme.getNexts())
lastState.addNext(asme);
/* Remove the call in the list of elements */
elements.remove(curAsme);
/* Use the translated function call's first element as current element */
curAsme = firstState;
}
/* Add current element to the visited set */
visited.add(curAsme);
/* Loop through the next elements */
int i = 0;
if (curAsme.getNexts() != null) {
for (AvatarStateMachineElement asme : curAsme.getNexts()) {
/* Check if it is a function call */
if (asme instanceof AvatarLibraryFunctionCall) {
AvatarStateMachineElement replaceBy = callsTranslated.get(asme);
/* Check if function call has already been translated */
if (replaceBy != null) {
/* replace by the translated function call */
curAsme.removeNext(i);
curAsme.addNext(replaceBy);
/* new next element has been added at the end of the list so we need to fix i */
i--;
} else {
/* mark the function call and the current state to be visited */
toVisit.add(asme);
toVisit.add(curAsme);
visited.remove(curAsme);
}
} else
toVisit.add(asme);
i++;
}
}
}
}
/**
* Removes all empty transitions between two states.
* This concerns also the start state, and end states.
* DO NOT take into account code of states, and start states
*
* @param block The block containing the state machine
* @param _canOptimize boolean data
*/
public void removeEmptyTransitions(AvatarBlock block, boolean _canOptimize) {
//TraceManager.addDev("Remove empty transitions with optimize=" + _canOptimize);
// Look for such a transition
// states -> tr -> state with tr is empty
// a tr is empty when it has no action or guard
AvatarStateElement foundState1 = null, foundState2 = null;
AvatarTransition foundAt = null;
for (AvatarStateMachineElement elt : elements) {
if ((elt instanceof AvatarStateElement) && (!(elt instanceof AvatarStartState))) {
if (elt.getNexts().size() == 1) {
if ((elt.getNext(0) instanceof AvatarTransition)) {
AvatarTransition at = (AvatarTransition) (elt.getNext(0));
if (at.getNext(0) instanceof AvatarStateElement) {
if (at.isEmpty() && at.hasNonDeterministicGuard()) {
if ((_canOptimize) && (!(elt.isCheckable()))) {
//TraceManager.addDev("State found:" + elt);
foundState1 = (AvatarStateElement) elt;
foundAt = at;
foundState2 = (AvatarStateElement) (at.getNext(0));
break;
}
}
}
} else {
TraceManager.addDev("Malformed spec: " + elt.getName() + " is followed by " + elt.getName());
}
}
}
}
// Found?
if (foundState1 != null) {
if (foundState1 == foundState2) {
// We simply remove the transition
//TraceManager.addDev("Found same state -> removing the transitions");
removeElement(foundAt);
// removing from the next of foundState1
foundState1.removeNext(foundAt);
} else {
// Must remove state1 and at, and link all previous of state 1 to state2
//TraceManager.addDev("Found 2 states state1=" + foundState1.getName() + " state2=" + foundState2.getName());
for (AvatarStateMachineElement elt : getPreviousElementsOf(foundState1)) {
elt.replaceAllNext(foundState1, foundState2);
}
removeElement(foundAt);
removeElement(foundState1);
foundState2.addReferenceObjectFrom(foundState1);
}
removeEmptyTransitions(block, _canOptimize);
}
}
public void removeEmptyTransitionsOnSameState(AvatarBlock _block) {
ArrayList<AvatarStateMachineElement> toBeRemoved = new ArrayList<>();
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition)elt;
if (at.isEmpty()) {
AvatarStateMachineElement previous = getPreviousElementOf(at);
AvatarStateMachineElement next = at.getNext(0);
if (( previous != null) && (next != null)) {
if ((previous == next) && (previous instanceof AvatarState)) {
toBeRemoved.add(at);
}
}
}
}
}
for(AvatarStateMachineElement elt : toBeRemoved) {
AvatarStateMachineElement previous = getPreviousElementOf(elt);
if (previous != null) {
previous.removeNext(elt);
}
elements.remove(elt);
}
}
// groups together transitions to avoid extra states
public void groupUselessTransitions(AvatarBlock _block) {
ArrayList<AvatarState> toBeRemoved = new ArrayList<>();
AvatarTransition atBefore, atAfter;
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarState) {
if ((elt.nexts.size() == 1) && (getPreviousElementsOf(elt).size() == 1)) {
// We need to check that the follower is only a transition with an action
AvatarStateMachineElement follower = elt.nexts.get(0);
if ((follower instanceof AvatarTransition) && (follower.nexts.size() == 1)) {
if (follower.nexts.get(0) instanceof AvatarState) {
AvatarStateMachineElement previous = getPreviousElementsOf(elt).get(0);
if (previous instanceof AvatarTransition) {
toBeRemoved.add((AvatarState) elt);
}
}
}
}
}
}
for (AvatarState st : toBeRemoved) {
atBefore = (AvatarTransition) (getPreviousElementsOf(st).get(0));
atAfter = (AvatarTransition) (st.nexts.get(0));
// Adding guard, delays and actions from after to before
if (atAfter.getGuard().isGuarded()) {
if (atBefore.getGuard().isGuarded()) {
atBefore.setGuard("[(" + atAfter.getGuard() + ") && (" + atBefore.getGuard() + ")]", "");
} else {
atBefore.setGuard(atAfter.getGuard(), "");
}
}
if (atAfter.hasDelay()) {
if (atBefore.hasDelay()) {
atBefore.setDelays(atAfter.getMinDelay() + "+" + atBefore.getMinDelay(),
atAfter.getMaxDelay() + "+" + atBefore.getMaxDelay(), "", "");
}
}
for (AvatarAction a : atAfter.getActions()) {
atBefore.addAction(a, a.getOriginalAction());
}
// Updating links
atBefore.nexts.clear();
atBefore.nexts.add(atAfter.getNext(0));
// Removing state and next transition
elements.remove(atAfter);
elements.remove(st);
}
}
public int getIndexOfStartState() {
if (allStates == null) {
return -1;
}
for (int i = 0; i < allStates.length; i++) {
if (allStates[i] instanceof AvatarStartState) {
return i;
}
}
return -1;
}
public int getIndexOfState(AvatarStateElement _ase) {
if (allStates == null) {
return -1;
}
for (int i = 0; i < allStates.length; i++) {
if (allStates[i] == _ase) {
return i;
}
}
return -1;
}
// Fills the current state machine by cloning the current one
public void advancedClone(AvatarStateMachine _newAsm, AvatarStateMachineOwner _newBlock) {
// Elements
HashMap<AvatarStateMachineElement, AvatarStateMachineElement> correspondenceMap = new HashMap<AvatarStateMachineElement, AvatarStateMachineElement>();
for (AvatarStateMachineElement elt : elements) {
AvatarStateMachineElement ae;
ae = elt.basicCloneMe(_newBlock);
/*if (ae instanceof AvatarState) {
TraceManager.addDev("New state: ");
}
TraceManager.addDev("elt: " + ae.toString());*/
if (ae == null) {
TraceManager.addDev("Null AE");
}
_newAsm.addElement(ae);
if (ae instanceof AvatarStartState) {
_newAsm.setStartState((AvatarStartState) ae);
}
correspondenceMap.put(elt, ae);
}
// Other attributes
for (AvatarStateMachineElement elt : elements) {
AvatarStateMachineElement ae = correspondenceMap.get(elt);
if (ae != null) {
elt.fillAdvancedValues(ae, correspondenceMap, this);
} else {
TraceManager.addDev("Null correspondance ae");
}
}
}
public AvatarTransition findEmptyTransition(final AvatarStateMachineElement elementSource,
final AvatarStateMachineElement elementTarget) {
for (final AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
final AvatarTransition transition = (AvatarTransition) element;
if (transition.isEmpty() && !transition.getNexts().isEmpty()) {
if (getPreviousElementOf(transition) == elementSource && transition.getNexts().get(0) == elementTarget) {
return transition;
}
}
}
}
return null;
}
public void removeAllDelays() {
for (final AvatarStateMachineElement element : elements) {
if (element instanceof AvatarTransition) {
final AvatarTransition transition = (AvatarTransition) element;
if (transition.hasDelay()) {
transition.setDelays("0", "0", transition.getOriginalMinDelay(), transition.getOriginalMaxDelay());
}
} else if (element instanceof AvatarSetTimer) {
((AvatarSetTimer) element).setTimerValue("0");
}
}
}
/**
* Looks for all numerical values over
* the provided max
*
* @param maxV Maximum value.
*/
public ArrayList<AvatarElement> elementsWithNumericalValueOver(int maxV) {
String val;
ArrayList<AvatarElement> invalids = new ArrayList<AvatarElement>();
for (AvatarStateMachineElement asme : elements) {
// Action on signals
if (asme instanceof AvatarActionOnSignal) {
AvatarActionOnSignal aaos = (AvatarActionOnSignal) asme;
for (int i = 0; i < aaos.getNbOfValues(); i++) {
val = aaos.getValue(i);
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
break;
}
}
}
if (asme instanceof AvatarRandom) {
AvatarRandom arand = (AvatarRandom) asme;
val = arand.getMinValue();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
} else {
val = arand.getMaxValue();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
}
}
if (asme instanceof AvatarSetTimer) {
AvatarSetTimer atop = (AvatarSetTimer) asme;
val = atop.getTimerValue();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
}
if (asme instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) asme;
// Guard
if (at.getGuard() != null) {
val = at.getGuard().toString();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
}
// Delays
val = at.getMinDelay();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
val = at.getMaxDelay();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
// Actions
for (AvatarAction aa : at.getActions()) {
val = aa.toString();
if (MyMath.hasIntegerValueOverMax(val, maxV)) {
invalids.add(this);
}
}
}
}
return invalids;
}
public List<ArrayList<AvatarTransition>> checkStaticInternalLoops() {
if (allStates == null) {
return null;
}
List<ArrayList<AvatarTransition>> loops = new ArrayList<ArrayList<AvatarTransition>>();
List<AvatarTransition> trace = new ArrayList<AvatarTransition>();
Set<AvatarStateMachineElement> visited = new HashSet<AvatarStateMachineElement>();
for (AvatarStateElement state : allStates) {
checkStaticInternalLoopsRec(state, state, trace, visited, loops, 0);
visited.add(state); //avoid cycles permutations
}
return loops;
}
public void checkStaticInternalLoopsRec(AvatarStateMachineElement node, AvatarStateMachineElement arrival, List<AvatarTransition> trace, Set<AvatarStateMachineElement> visited, List<ArrayList<AvatarTransition>> loops, int depth) {
if (visited.contains(node)) {
if (node == arrival) {
//valid loop, copy trace to loops
loops.add(new ArrayList<AvatarTransition>(trace));
return;
} else {
//not valid loop
return;
}
} else if (node.nexts == null) {
return;
}
visited.add(node);
for (AvatarStateMachineElement next : node.nexts) {
if (next instanceof AvatarTransition) {
AvatarTransition at = (AvatarTransition) next;
//the state machine should alternate states and transitions
if (!(at.getNext(0) instanceof AvatarActionOnSignal)) {
//Choose internal action paths
trace.add(depth, at);
checkStaticInternalLoopsRec(at.getNext(0), arrival, trace, visited, loops, depth + 1);
trace.remove(depth);
}
}
}
visited.remove(node);
return;
}
// Returns the list of elements non reachabable from the start state
public List<AvatarStateMachineElement> getUnusedElements() {
if (startState == null) {
return null;
}
List<AvatarStateMachineElement> elts = new LinkedList<>();
HashSet<AvatarStateMachineElement> found = new HashSet<>();
findAvatarElements(startState, found);
for (AvatarStateMachineElement asme : elements) {
if (!(found.contains(asme))) {
elts.add(asme);
}
}
return elts;
}
private void findAvatarElements(AvatarStateMachineElement first, HashSet<AvatarStateMachineElement> found) {
if (found.contains(first)) {
return;
}
found.add(first);
if (first instanceof AvatarState) {
// Find all internal start states of this state
for (AvatarStateMachineElement internal : elements) {
if (internal instanceof AvatarStartState) {
if (internal.getState() == first) {
findAvatarElements(internal, found);
}
}
}
}
for (AvatarStateMachineElement asme : first.getNexts()) {
findAvatarElements(asme, found);
}
}
public boolean removeDuplicatedTransitions() {
ArrayList<AvatarStateMachineElement> toBeRemoved = new ArrayList<>();
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarState) {
// We look at the nexts
// If transition -> state duplicated, or transition -> aaos duplicated -> state : we remove the duplicate
toBeRemoved.addAll(removeDuplicatedTransitionsFromState((AvatarState) elt));
}
}
for (AvatarStateMachineElement asme : toBeRemoved) {
elements.remove(asme);
}
for (AvatarStateMachineElement elt : elements) {
if (elt instanceof AvatarState) {
toBeRemoved.clear();
for (AvatarStateMachineElement asmeState: elt.getNexts()) {
if (!(elements.contains(asmeState))) {
toBeRemoved.add(asmeState);
}
}
for (AvatarStateMachineElement asme : toBeRemoved) {
elt.getNexts().remove(asme);
}
}
}
return toBeRemoved.size() != 0;
}
public ArrayList<AvatarStateMachineElement> removeDuplicatedTransitionsFromState(AvatarState _st) {
ArrayList<AvatarStateMachineElement> toBeRemoved = new ArrayList<>();
// At least two exiting transitions
if (_st.getNexts().size() < 2) {
return toBeRemoved;
}
// We check is the at least two transitions are equivalent
for (int i = 0; i < _st.getNexts().size(); i++) {
AvatarTransition at1 = (AvatarTransition) (_st.getNexts().get(i));
for (int j = i + 1; j < _st.getNexts().size(); j++) {
AvatarTransition at2 = (AvatarTransition) (_st.getNexts().get(j));
if (at1.equals(at2)) {
//TraceManager.addDev("\tTwo equal transitions in state " + _st.getName());
// We have to consider the next of at1 and at2
AvatarStateMachineElement next1, next2;
next1 = at1.getNext(0);
next2 = at2.getNext(0);
if (next1 instanceof AvatarState && next1 == next2) {
toBeRemoved.add(at2);
} else if ((next1 instanceof AvatarActionOnSignal) && (next2 instanceof AvatarActionOnSignal)) {
//TraceManager.addDev("\tChecking for equal AAOS " + _st.getName());
AvatarActionOnSignal aaos1 = (AvatarActionOnSignal) next1;
AvatarActionOnSignal aaos2 = (AvatarActionOnSignal) next2;
if (next1.getNexts().get(0) instanceof AvatarTransition && next2.getNexts().get(0) instanceof AvatarTransition) {
AvatarTransition at11 = (AvatarTransition) next1.getNexts().get(0);
AvatarTransition at21 = (AvatarTransition) next2.getNexts().get(0);
if (at11.equals(at21)) {
if (at11.getNexts().get(0) instanceof AvatarState && (at11.getNexts().get(0) == at21.getNexts().get(0))) {
// We need to compare the two AvatarActionOnSignal
//TraceManager.addDev("\tComparing aaos1 and aaos2 in " + _st.getName());
if (aaos1.equals(aaos2)) {
toBeRemoved.add(at2);
toBeRemoved.add(aaos2);
toBeRemoved.add(at21);
}
}
}
}
}
}
}
}
TraceManager.addDev("\ttoBeRemoved of size " + toBeRemoved.size() + " for state " + _st.getName());
return toBeRemoved;
}
}