diff --git a/src/main/java/avatartranslator/tosysmlv2/Avatar2SysML.java b/src/main/java/avatartranslator/tosysmlv2/Avatar2SysML.java index aeb17fbc0342faf07a7c6e242e286b9374336c50..61f2f7e922e80748f317f70bc5feaeee791f22a0 100644 --- a/src/main/java/avatartranslator/tosysmlv2/Avatar2SysML.java +++ b/src/main/java/avatartranslator/tosysmlv2/Avatar2SysML.java @@ -603,6 +603,13 @@ public class Avatar2SysML { (fifoSet.contains(as) ? "'#Fifo'" : "'#Channel'") + ";\n"); } + /** generates SysML declarations for all blocks: iterate on specification block list */ + private void blocks2SysML(){ + avsysml.append("\n" + indentation + "// BLOCKS $$$$$$$$$$$$$$$$$$$$$$$$\n"); + for(AvatarBlock block: avspec.getListOfBlocks()) + if (block.getFather() == null) block2SysML(block); + } + /** generates SysML declarations for a blocks and direct access to inner blocks through their name (not their full path) */ private void block2SysML(AvatarBlock block){ String blockSYSMLname = blockSysMLname(block.getName()); @@ -721,12 +728,6 @@ public class Avatar2SysML { unchainBlock(); } - /** generates SysML declarations for all blocks: iterate on specification block list */ - private void blocks2SysML(){ - avsysml.append("\n" + indentation + "// BLOCKS $$$$$$$$$$$$$$$$$$$$$$$$\n"); - for(AvatarBlock block: avspec.getListOfBlocks()) - if (block.getFather() == null) block2SysML(block); - } /** generates state information for each state machine element of the specification and technically * added states (before some communication elements). Put this information in a map which is returned. @@ -802,12 +803,12 @@ public class Avatar2SysML { } /** buffer to build SysML declarations of outgoing transitions from one state(filled by method transitionsAndRequests) */ - StringBuffer sysMLtransitions = new StringBuffer(); + private StringBuffer sysMLtransitions = new StringBuffer(); /** buffer to build in-a-state requests associated to the outgoing transitions of this state (filled by method transitionsAndRequests) */ - StringBuffer sysMLrequests = new StringBuffer(); + private StringBuffer sysMLrequests = new StringBuffer(); /** communication (and timer) states that require an added state before (as they have incomming transitions with action, from the currently * handled state) (filled by method transitionsAndRequests) */ - List<AvatarStateMachineElement> requirePreCom = new ArrayList<AvatarStateMachineElement>(); + private List<AvatarStateMachineElement> requirePreCom = new ArrayList<AvatarStateMachineElement>(); /** to iterate on the state machine elements of one block. An iteration puts a state declaration in avsysml, followed by the declarations of * its outgoing transitions. If the needed technically added pre-communication are detected while handling these transitions. The declarations @@ -970,7 +971,7 @@ public class Avatar2SysML { avsysml.append(indentation + "'@request' =\n"); indent(1); // receiving request from pre-receive state to receive state - avsysml.append(receiveRequest2SysML(1, "0", "0", signalinfo) + "\n"); + avsysml.append(receiveRequest2SysML(1, "0", "0", signalinfo.getName()) + "\n"); indentation = indentation.substring(2 * indentStepSize); avsysml.append(indentation + ");\n"); // transition from pre-receive state to receive state @@ -996,7 +997,7 @@ public class Avatar2SysML { indent(1); // put the specific sending request, carrying a value avsysml.append(setTimerRequest2SysML(1, "0", "0", - timerBlockSysMLname(((AvatarSetTimer) aos).getTimer().getName()) + ".'@set'" , + timerBlockSysMLname(((AvatarSetTimer) aos).getTimer().getName()) , ((AvatarSetTimer)aos).getTimerValue()) + "\n"); unindent(2); avsysml.append(indentation + ");\n"); @@ -1013,7 +1014,7 @@ public class Avatar2SysML { indent(1); // put the specific sending request, carrying no value avsysml.append(resetTimerRequest2SysML(1, "0", "0", - timerBlockSysMLname(((AvatarResetTimer) aos).getTimer().getName()) + ".'@reset'" ) + "\n"); + timerBlockSysMLname(((AvatarResetTimer) aos).getTimer().getName())) + "\n"); indentation = indentation.substring(2 * indentStepSize); avsysml.append(indentation + ");\n"); // put the associated transition @@ -1029,7 +1030,7 @@ public class Avatar2SysML { indent(1); // put the specific receiving request, carrying no value avsysml.append(expireTimerRequest2SysML(1, "0", "0", - timerBlockSysMLname(((AvatarExpireTimer) aos).getTimer().getName()) + ".'@expire'" ) + "\n"); + timerBlockSysMLname(((AvatarExpireTimer) aos).getTimer().getName())) + "\n"); indentation = indentation.substring(2 * indentStepSize); avsysml.append(indentation + ");\n"); // put the associated transition @@ -1045,7 +1046,7 @@ public class Avatar2SysML { } /** distribution law information, to put at the end of associated the transition declarations */ - String endTransition(int delayDistributionLaw, String delayExtra1, String delayExtra2, double probability){ + private String endTransition(int delayDistributionLaw, String delayExtra1, String delayExtra2, double probability){ if (delayDistributionLaw == DELAY_UNIFORM_LAW && probability == 1) return ";\n"; // nothing because default @@ -1073,6 +1074,7 @@ public class Avatar2SysML { result.append(indentation + "}\n"); return result.toString(); } + /** computes transition declarations and request descriptions for the outgoing transitions of the state. Put the corresponding * texts in the sysMLtransitions and sysMLrequests StringBuffers. While doing this, identify communication states that require * an added pre-communication state. @@ -1081,8 +1083,8 @@ public class Avatar2SysML { * @param poolName "pool" or "request" depending on the number of allowed outgoing transitions * @param stateMap to find state names */ - void transitionsAndRequests(String srcName, List<AvatarStateMachineElement> nexts, String poolName, - HashMap<AvatarStateMachineElement,StateInfo> stateMap) { + private void transitionsAndRequests(String srcName, List<AvatarStateMachineElement> nexts, String poolName, + HashMap<AvatarStateMachineElement, StateInfo> stateMap) { // initialization requirePreCom.clear(); sysMLtransitions.delete(0, sysMLtransitions.length()); @@ -1121,54 +1123,74 @@ public class Avatar2SysML { // wrap with parenthesis sysMLrequests.insert(0, indentation + poolName + " = (\n"); sysMLrequests.append(indentation + ")"); - } + } - private String clean_guard(String _guard) { + /** remove wrapping brackets of a guard (if exist)*/ + private String clean_guard(String _guard) { int open = _guard.indexOf('['); int close = _guard.lastIndexOf(']'); if (open != -1 && close != -1 && close > open) return _guard.substring(open+1, close); else return _guard; - } - // index is 0 if transition is alone - void transitionAndRequest(String srcName, AvatarTransition at, int index, HashMap<AvatarStateMachineElement, StateInfo> stateMap){ - int transindex = ((index == 0) ? 1 : index); + } - // identifying cases + /** add a transition declaration in sysMLtransitions and the associated request description in sysMLrequests. Ii the the + * transition has actions and the transition's target is a communication, then this target is identified as requiring an + * added pre-communication state (added to requirePreCom). In the produced SysML model, transitions are ordered with indexes + * beginning at 1. + * + * @param srcName source of the transition + * @param at the transition to handle + * @param index index of the transition, or 0 if transition is alone (real index is 1) + * @param stateMap to make state names available + */ + private void transitionAndRequest(String srcName, AvatarTransition at, int index, HashMap<AvatarStateMachineElement, StateInfo> stateMap){ + int transindex = ((index == 0) ? 1 : index); // true index + + // identify cases and instantiate case specific parameters for the parametrized handling that follows ................ + + // declare parameters boolean guarded = !at.hasNonDeterministicGuard(); AvatarStateMachineElement target = at.getNext(0); - String tgtName; + String tgtName; // SysML name of transition's target (in SysML model, may differ from Avatar model, due to added states) int requestType = 0; // 0:trivial, 1:Send, 2:Receive, 3:SetTimer, 4: ResetTimer, 5:ExpireTimer + // computes parameters if((at.getActions()!=null && at.getActions().size()!=0) && - (target instanceof AvatarActionOnSignal || target instanceof AvatarTimerOperator)) { // preCommunication Required + (target instanceof AvatarActionOnSignal || target instanceof AvatarTimerOperator)) { + // added pre-communication state required before target (in avatar) which is communication. Type: trivial. requirePreCom.add(target); - tgtName = stateMap.get(target).getPreName(); + tgtName = stateMap.get(target).getPreName(); // transition's target is the added state } else { - tgtName = stateMap.get(target).getName(); - if(target instanceof AvatarActionOnSignal){ + tgtName = stateMap.get(target).getName(); // transition's target is the same as in Avatar + + if(target instanceof AvatarActionOnSignal){ // communication if (((AvatarActionOnSignal)target).isSending()) - requestType = 1; + requestType = 1; // Send else - requestType = 2; + requestType = 2; // Receive } else if (target instanceof AvatarSetTimer) - requestType = 3; + requestType = 3; // SetTimer else if (target instanceof AvatarResetTimer) - requestType = 4; + requestType = 4; // ResetTimer else if (target instanceof AvatarExpireTimer) - requestType = 5; + requestType = 5; // ExpireTimer } - // computing request + + // put transition's request respecting parameters..................................... + if (guarded) { - System.out.println("§§§§§§§§§§§§§§§§§§ " + clean_guard(((AvatarTransition)at).getOriginalGuard()) + " §§§§§§§§§§§§§§§"); sysMLrequests.append(indentation + "if " + expr2SysML(clean_guard(((AvatarTransition)at).getOriginalGuard())) + " ?\n"); indent(1); } + // delay don't depend on parameters String minDelay = ( at.getOriginalMinDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMinDelay()) ); String maxDelay = ( at.getOriginalMaxDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMaxDelay()) ); + + // call the type specific compute-request method and put request if(requestType == 0) // Trivial sysMLrequests.append(trivialRequest2SysML(transindex, minDelay, @@ -1183,7 +1205,7 @@ public class Avatar2SysML { sysMLrequests.append(receiveRequest2SysML(transindex, minDelay, maxDelay, - methodMap.get(((AvatarActionOnSignal)target).getSignal()))); + methodMap.get(((AvatarActionOnSignal)target).getSignal()).getName())); else if (requestType == 3) // Set sysMLrequests.append(setTimerRequest2SysML(transindex, minDelay, @@ -1194,137 +1216,207 @@ public class Avatar2SysML { sysMLrequests.append(resetTimerRequest2SysML(transindex, minDelay, maxDelay, - timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()) + ".'@reset'" )); + timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()))); else // Expire sysMLrequests.append(expireTimerRequest2SysML(transindex, minDelay, maxDelay, - timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()) + ".'@expire'" )); + timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()))); + if(guarded) { unindent(1); sysMLrequests.append("\n" + indentation + "else '#nok_request'(" + transindex + ")"); } - // computing transition - indentation = indentation.substring(2 * indentStepSize); - String doAction; + // put transition's declaration ........................................ + + unindent(2); // to reach indent level of request declaration + + // compute transition'actions + String doAction; // the SysML action part of the transition indent(1); - if(requestType == 2) + if(requestType == 2) // compute receive specific actions (variable update). No other transition actions thanks to added states. doAction = receiveActions2SysML(methodMap.get(((AvatarActionOnSignal)target).getSignal()), ((AvatarActionOnSignal)target).getOriginalValues()); - else + else // computes actions from the Avatar transition's action list doAction = transitionActions2SysML(at); unindent(1); + // put transition declaration + // header sysMLtransitions.append("\n" + indentation + "transition : '#AvatarTransition' first " + srcName); - if(index > 0) + // index + if(index > 0) // not default thus explicit index sysMLtransitions.append(" if '@index' == " + index + "\n"); - else + else // default thus implicit index sysMLtransitions.append("\n"); + // actions indent(1); - if (doAction == null || doAction.length() == 0) + if (doAction == null || doAction.length() == 0) // no action, put target and distribution law information sysMLtransitions.append(indentation + "then " + tgtName + endTransition(at.getDelayDistributionLaw(), at.getDelayExtra1(), at.getDelayExtra2(), at.getProbability())); - else { + else { // put actions, target and distribution law information sysMLtransitions.append(doAction); sysMLtransitions.append(" then " + tgtName + endTransition(at.getDelayDistributionLaw(),at.getDelayExtra1(),at.getDelayExtra2(), at.getProbability())); } indent(1); } - String trivialRequest2SysML(int index, String min, String max) { - if (max.equals("0")) - if (min.equals("0")) - if (index == 1) + + /** computes request to non-communication state + * + * @param index request index + * @param min minimal delay. not null and not empty (must be set to "0" in these cases) + * @param max maximal delay. not null and not empty (must be set to "0" in these cases) + * @return text of the relevant SysML call to the relevant SysML constructor + */ + private String trivialRequest2SysML(int index, String min, String max) { + if (max.equals("0")) // no delay or simple delay + if (min.equals("0")) // no delay + if (index == 1) // default thus implicit return indentation + "'#immediate_request'"; - else + else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ")"; - else - if (index == 1) + else // simple delay + if (index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = " + min + ")"; - else + else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = " + min + ")"; - else if (max.trim().equals(min.trim())) - if(index == 1) + else if (max.trim().equals(min.trim())) // simple delay (equal bounds) + if(index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = " + min + ")"; - else + else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = " + min + ")"; - else - if(index == 1) + else // range delay + if(index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = '#bound_random'(" + min + ", " + max + "))"; - else + else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = '#bound_random'(" + min + ", " + max + "))"; } - String sendRequest2SysML(int index, String min, String max, MethodInfo signalInfo, List<String> values) { + /** computes request to send state + * + * @param index request index + * @param min minimal delay. not null and not empty (must be set to "0" in these cases) + * @param max maximal delay. not null and not empty (must be set to "0" in these cases) + * @param signalInfo name and profile information about the concerned signal + * @param values strings denoting send parameter values + * @return text of the relevant SysML call to the relevant SysML constructor + */ + private String sendRequest2SysML(int index, String min, String max, MethodInfo signalInfo, List<String> values) { + // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#SendRequest'(\n"); indent(1); - if (index != 1) + + // index + if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); + + // signal (equivalent to channel in SysML) result.append(indentation + "'@channel'= " + signalInfo.getName() + ",\n"); - if (max.equals("0")) { - if (!min.equals("0")) + + // delay + if (max.equals("0")) { // no delay or simple delay + if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } - else if (max.trim().equals(min.trim())) + else if (max.trim().equals(min.trim())) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); - else + else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); + // payload int nbFields = signalInfo.getArity(); - if (nbFields == 0) + if (nbFields == 0) // simple SysML constructor call result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "()\n"); - else { + else { // SysML constructor call with parameters result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "(\n"); indent(1); int j = 0; - while (j < nbFields) { //for(String vl : values) + while (j < nbFields) { // iterate on parameters result.append(indentation + expr2SysML(values.get(j)) + ",\n"); j++; } result.replace(result.length()-2, result.length(), " )\n"); unindent(1); } + unindent(1); result.append(indentation + ")"); return result.toString(); } - String setTimerRequest2SysML(int index, String min, String max, String chname, String value) { + + + /** computes request to set-timer state + * + * @param index request index + * @param min minimal delay. not null and not empty (must be set to "0" in these cases) + * @param max maximal delay. not null and not empty (must be set to "0" in these cases) + * @param timer name of the concerned timer + * @param value string denoting the duration until timer expiration (countdown) + * @return text of the relevant SysML call to the relevant SysML constructor + */ + private String setTimerRequest2SysML(int index, String min, String max, String timer, String value) { + // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarSetTimerRequest'(\n"); indent(1); - if (index != 1) + + // index + if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); - result.append(indentation + "'@channel'= " + chname + ",\n"); - if (max.equals("0")) { - if (!min.equals("0")) + + // setting a timer is sending a message on a dedicated "@set" channel of the timer block + result.append(indentation + "'@channel'= " + timer + ".'@set'" + ",\n"); + + // delay + if (max.equals("0")) { // no delay or simple delay + if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } - else if (max.equals(min)) + else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); - else + else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); + // payload result.append(indentation + "'@payload' = '#TimerSetMsg'(" + value + ")\n"); unindent(1); result.append(indentation + ")"); return result.toString(); } - String resetTimerRequest2SysML(int index, String min, String max, String chname) { + + /** computes request to reset-timer state + * + * @param index request index + * @param min minimal delay. not null and not empty (must be set to "0" in these cases) + * @param max maximal delay. not null and not empty (must be set to "0" in these cases) + * @param timer name of the concerned timer + * @return text of the relevant SysML call to the relevant SysML constructor + */ + private String resetTimerRequest2SysML(int index, String min, String max, String timer) { + // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarResetTimerRequest'(\n"); indent(1); - if (index != 1) + + //index + if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); - result.append(indentation + "'@channel'= " + chname + ",\n"); - if (max.equals("0")) { - if (!min.equals("0")) + + // resetting a timer is sending a message on a dedicated "@reset" channel of the timer block + result.append(indentation + "'@channel'= " + timer + ".'@reset'" + ",\n"); + + // delay + if (max.equals("0")) { // no delay or simple delay + if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } - else if (max.equals(min)) + else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); - else + else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); + // payload (simple message without value) result.append(indentation + "'@payload' = '#TimerResetMsg'()\n"); unindent(1); @@ -1332,86 +1424,120 @@ public class Avatar2SysML { return result.toString(); } - String receiveRequest2SysML(int index, String min, String max, MethodInfo chinfo) { + /** computes request to send state + * + * @param index request index + * @param min minimal delay. not null and not empty (must be set to "0" in these cases) + * @param max maximal delay. not null and not empty (must be set to "0" in these cases) + * @param signalName name of the concerned signal + * @return text of the relevant SysML call to the relevant SysML constructor + */ + private String receiveRequest2SysML(int index, String min, String max, String signalName) { + // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#ReceiveRequest'(\n"); indent(1); - if (index != 1) + + // index + if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); - result.append(indentation + "'@channel'= " + chinfo.getName()); - if (max.equals("0")) - if (!min.equals("0")) + + // signal (equivalent to channel in SysML) + result.append(indentation + "'@channel'= " + signalName); + + // delay + if (max.equals("0")) // no delay or simple delay + if (!min.equals("0")) // simple delay result.append(",\n" + indentation + "'@delay' = " + min + "\n"); - else + else // no delay result.append("\n"); - else if (max.equals(min)) + else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); - else + else // range delay result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n"); + unindent(1); result.append(indentation + ")"); return result.toString(); } - String expireTimerRequest2SysML(int index, String min, String max, String chname) { + + private String expireTimerRequest2SysML(int index, String min, String max, String chname) { + // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarExpireTimerRequest'(\n"); indent(1); - if (index != 1) + + // index + if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); - result.append(indentation + "'@channel'= " + chname); - if (max.equals("0")) - if (!min.equals("0")) + + // timer expiration is detected by receiving a message on a dedicated "@expire" channel of the timer block + result.append(indentation + "'@channel'= " + chname + ".'@expire'"); + + // delay + if (max.equals("0")) // no delay or simple delay + if (!min.equals("0")) // simple delay result.append(",\n" + indentation + "'@delay' = " + min + "\n"); - else + else // no delay result.append("\n"); - else if (max.equals(min)) + else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); - else + else // range delay result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n"); + unindent(1); result.append(indentation + ")"); return result.toString(); } - String methodCall2SysML(AvatarTermFunction m) { - return expr2SysML(m.getOriginalAction()); - } + /** computes the action part of the SysML transition associated to an Avatar transition */ String transitionActions2SysML(AvatarTransition at) { StringBuffer result; + int size = at.getNbOfAction(); if (size == 0) return null; + + // header result = new StringBuffer(indentation + "do action : '#TransitionAction' {\n" + indentation + indentStep + "first start;\n"); indent(1); - for(int i = 0; i < size; i++) { + for(int i = 0; i < size; i++) { // iterate on actions of the Avatar transition String ac = at.getOriginalAction(i); int eq = ac.indexOf("="); - if(eq != -1) { - String lh = leftHandSysMLname(ac.substring(0,eq).trim()); - String rh = expr2SysML(ac.substring(eq+1).trim()); + if(eq != -1) { // assignment action + String lh = leftHandSysMLname(ac.substring(0,eq).trim()); // lefthand + String rh = expr2SysML(ac.substring(eq+1).trim()); // righthand result.append(indentation + "then assign " + lh + ":= " + rh + ";\n"); } - else { + else { // method call result.append(indentation + "then action = " + expr2SysML(ac) + ";\n"); } } result.append(indentation + "then done;\n"); + unindent(1); result.append(indentation + "}"); return result.toString(); } + /** computes the action part of the SysML transition associated to an Avatar transition leading to a receive state. Due to the introduction of + * pre-communication states (when required), there are no other actions than the updating of variables w.r.t. the received message */ String receiveActions2SysML(MethodInfo signalInfo, List<String> values) { - if (values == null || values.size() == 0) return ""; + if (values == null || values.size() == 0) return ""; // simple signal without value + StringBuffer result = new StringBuffer(indentation + "do action : '#ReceiveAction' {\n"); indent(1); + + // put access to the received message result.append(indentation + "item '@msg' : " + signalInfo.getMessageType() + " = '@payload' as " + signalInfo.getMessageType() + ";\n" + indentation + "first start;\n"); + int nbFields = signalInfo.getArity(); int j = 0; - while (j < nbFields) { + while (j < nbFields) { // iterate on message fields result.append(indentation + "then assign " + leftHandSysMLname(values.get(j)) + " := '@msg'." + signalInfo.getFieldName(j) + ";\n"); j++; } result.append(indentation + "then done;\n"); + unindent(1); result.append(indentation + "}"); return result.toString();