Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2c09bd179811171e471b39450cb5e6681dc1b03e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
/*******************************************************************************
 * Copyright (c) 2014 protos software gmbh (http://www.protos.de).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * CONTRIBUTORS:
 * 		Henrik Rentz-Reichert (initial contribution)
 *
 *******************************************************************************/

package org.eclipse.etrice.generator.fsm.generic

import com.google.inject.Inject
import org.eclipse.etrice.core.fsm.fSM.ComponentCommunicationType
import org.eclipse.etrice.core.fsm.fSM.GuardedTransition
import org.eclipse.etrice.core.fsm.fSM.MessageFromIf
import org.eclipse.etrice.core.fsm.fSM.ModelComponent
import org.eclipse.etrice.core.fsm.fSM.State
import org.eclipse.etrice.core.fsm.fSM.Transition
import org.eclipse.etrice.core.fsm.fSM.TransitionPoint
import org.eclipse.etrice.core.fsm.naming.FSMNameProvider
import org.eclipse.etrice.core.fsm.util.FSMHelpers
import org.eclipse.etrice.core.genmodel.fsm.TriggerExtensions
import org.eclipse.etrice.core.genmodel.fsm.fsmgen.CommonTrigger
import org.eclipse.etrice.core.genmodel.fsm.fsmgen.GraphContainer
import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Link
import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Node
import org.eclipse.etrice.generator.fsm.base.CodegenHelpers
import org.eclipse.xtext.util.Pair

import static org.eclipse.xtext.util.Tuples.*

import static extension org.eclipse.etrice.core.genmodel.fsm.FsmGenExtensions.*
import java.util.ArrayList

/**
 * @author Henrik Rentz-Reichert
 *
 */
abstract class AbstractStateMachineGenerator {

	protected static val NUM_PREDEF_STATE_CONSTANTS = 2
	
	@Inject public extension FSMHelpers
	@Inject public extension CodegenHelpers
	@Inject public extension FSMExtensions
	@Inject public ILanguageExtensionBase langExt
	@Inject public IMessageIdGenerator msgIdGen
	@Inject public IIfItemIdGenerator itemIdGen
	@Inject public TransitionChainGenerator transitionChainGenerator
	@Inject public IDetailCodeTranslator translator
	@Inject public FSMNameProvider fsmNameProvider

	/**
	 * generates trigger IDs.
	 * Inheritance (if available) is used for base class IDs.
	 *
	 * @param gc the {@link GraphContainer}
	 * @return the generated code
	 */
	def String genTriggerConstants(GraphContainer gc) {
		gc.genTriggerConstants(langExt.usesInheritance)
	}

	/**
	 * generates trigger IDs.
	 * Inheritance (if available) is used for base class IDs.
	 *
	 * @param gc the {@link GraphContainer}
	 * @param omitBase use <code>true</code> if no base class trigger constants are needed
	 *
	 * @return the generated code
	 */
	def String genTriggerConstants(GraphContainer gc, boolean omitBase) {
		val triggers = if (omitBase)
					gc.component.ownMessagesFromInterfaces
					else gc.component.allMessagesFromInterfaces

		val list = <Pair<String, String>>newArrayList
		list.add(pair("POLLING", "0"));
		for (mif : triggers) {
			if (mif.from.eventDriven) {
				list.add(pair(mif.triggerCodeName, itemIdGen.getIfItemId(mif.from)+" + EVT_SHIFT*"+msgIdGen.getMessageID(mif)))
			}
		}

		return langExt.genEnumeration("triggers", list)
	}
	
	def String getTriggerCodeName(MessageFromIf mif) {
		return "TRIG_"+mif.from.name+"__"+fsmNameProvider.getMessageName(mif.message);
	}

	/**
	 * generates state ID constants.
	 * Inheritance (if available) is used for base class IDs.
	 *
	 * @param gc the {@link GraphContainer}
	 * @return the generated code
	 */
	def genStateIdConstants(GraphContainer gc) {
		gc.genStateIdConstants(langExt.usesInheritance)
	}

	/**
	 * generates state ID constants.
	 * Inheritance (if available) is used for base class IDs.
	 *
	 * @param gc the {@link GraphContainer}
	 * @param omitBase use <code>true</code> if no base class state constants are needed
	 *
	 * @return the generated code
	 */
	def genStateIdConstants(GraphContainer gc, boolean omitBase) {
		// with inheritance we exclude inherited states
		val allStateNodes = gc.graph.allStateNodes.toList // TODO: without toList this didn't work - why?
		
		// omitBase: base class constants are inherited, so we start at an offset of
		//         NUM_PREDEF_STATE_CONSTANTS + #(inherited states)
		// !omitBase: nothing is inherited, offset is just NUM_PREDEF_STATE_CONSTANTS
		var offset = NUM_PREDEF_STATE_CONSTANTS + if (omitBase)
			allStateNodes.filter[inherited].size else 0
		
		// considered are all states (case of !omitBase) or just the ones which are not inherited (case of omitBase)
		var consideredStates = (if (omitBase)
			allStateNodes.filter[!inherited] else allStateNodes).map[stateGraphNode].filter(typeof(State)).toList

		// we sort them with the non leaf states last to have the history as short as possible
		consideredStates = consideredStates.leafStatesLast.toList

		var list = <Pair<String, String>>newArrayList
		if (!omitBase) {
			list.add(pair("NO_STATE","0"))
			list.add(pair("STATE_TOP","1"))
		}
		for (state : consideredStates) {
			list.add(pair(state.getGenStateId, offset.toString))
			offset = offset+1;
		}
		list.add(pair("STATE_MAX", offset.toString))

		return langExt.genEnumeration("state_ids", list)
	}

	/**
	 * generates transition chain ID constants.
	 * Inheritance can't be used used for base class IDs because of corner cases
	 * where base class and derived class chain IDs deviate (see bug 501354).
	 *
	 * @param gc the {@link GraphContainer}
	 *
	 * @return the generated code
	 */
	def genTransitionChainConstants(GraphContainer gc) {
		gc.genTransitionChainConstants(false/*langExt.usesInheritance*/)
	}

	/**
	 * generates transition chain ID constants.<br/><br/>
	 * 
	 * <b>Note:</b> Inheritance can't be used used for base class IDs because of corner cases
	 * where base class and derived class chain IDs deviate.
	 *
	 * @param gc the {@link GraphContainer}
	 * @param omitBase use <code>true</code> if no base class transition chain constants are needed
	 *
	 * @return the generated code
	 */
	def genTransitionChainConstants(GraphContainer gc, boolean omitBase) {
		val chains = (if (omitBase)
			gc.graph.allLinks.filter[!inherited] else gc.graph.allLinks).map[transition].filter[isChainHead].filter(typeof(Transition)).toList
		var offset = if (omitBase)
			gc.graph.allLinks.filter[inherited].size else 0

		var list = <Pair<String, String>>newArrayList
		for (chain : chains) {
			offset = offset+1;
			list.add(pair(chain.genChainId, offset.toString))
		}

		return langExt.genEnumeration("ChainIDs", list)
	}

	/**
	 * generates entry and exit code for states
	 *
	 * @param gc the {@link GraphContainer}
	 * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration
	 *
	 * @return the generated code
	 */
	def String genEntryAndExitCodes(GraphContainer gc, boolean generateImplementation) {
		gc.genEntryAndExitCodes(generateImplementation, langExt.usesInheritance)
	}

	/**
	 * generates entry and exit code for states
	 *
	 * @param gc the {@link GraphContainer}
	 * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration
	 * @param omitBase use <code>true</code> if no base class entry and exit codes are needed
	 *
	 * @return the generated code
	 */
	def String genEntryAndExitCodes(GraphContainer gc, boolean generateImplementation, boolean omitBase) {
		val states = gc.graph.allStateNodes.filter[!omitBase || !inherited].toList
		'''
		«FOR state : states»
			«gc.genActionCodeMethods(state, generateImplementation)»
		«ENDFOR»
		'''
	}

	/**
	 * generates transition action codes
	 *
	 * @param gc the {@link GraphContainer}
	 * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration
	 *
	 * @return the generated code
	 */
	def String genActionCodes(GraphContainer gc, boolean generateImplementation) {
		gc.genActionCodes(generateImplementation, langExt.usesInheritance)
	}

	/**
	 * generates transition action codes
	 *
	 * @param gc the {@link GraphContainer}
	 * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration
	 * @param omitBase use <code>true</code> if no base class action codes are needed
	 *
	 * @return the generated code
	 */
	def String genActionCodes(GraphContainer gc, boolean generateImplementation, boolean omitBase) {
		val transitions = gc.graph.allLinks.filter[!omitBase || !inherited].filter[transition.action.hasDetailCode].toList
		'''
		«FOR tr : transitions»
			«gc.genActionCodeMethod(tr, generateImplementation)»
		«ENDFOR»
		'''
	}

	def String genStateSwitchMethods(GraphContainer gc, boolean generateImplementation) {
		val mc = gc.component
		val async = mc.commType==ComponentCommunicationType::ASYNCHRONOUS
		val eventDriven = mc.commType==ComponentCommunicationType::EVENT_DRIVEN
		val ifItemPtr = interfaceItemType + langExt.pointerLiteral
		val handleEvents = async || eventDriven
		val chainIDScope = if (langExt.usesInheritance) mc.className+langExt.scopeSeparator else ""
		val opScope = langExt.operationScope(mc.className, !generateImplementation)
		val opScopePriv = if (langExt.usesInheritance)
							opScope
						else
							""
		val publicIf = if (langExt.usesInheritance)
				langExt.accessLevelPublic
			else
				langExt.accessLevelPrivate
		val privAccess = langExt.accessLevelPrivate
		val selfPtr = langExt.selfPointer(mc.className, true)
		val selfOnly = langExt.selfPointer(mc.className, false)
		val getLocalId = 	if (langExt.usesInheritance) {
								if (langExt.usesPointers)
									"->getLocalId()"
								else
									".getLocalId()"
							}
							else
								"->localId"
		val constIfItemPtr = if (langExt.usesPointers)
								"const "+ifItemPtr
							else
								ifItemPtr
		val usesHdlr = usesHandlerTrPoints(gc)
		val nodes = gc.graph.allStateNodes.toList.sortBy[(stateGraphNode as State).genStateId]
		val state2node = newHashMap
		nodes.forEach[state2node.put(stateGraphNode as State, it)]
		val states = nodes.map[stateGraphNode].filter(typeof(State)).toList
		val transitionChains = gc.graph.allLinks.filter[isChainHead].toList.sortBy[transition.genChainId]
		'''
		/**
		 * calls exit codes while exiting from the current state to one of its
		 * parent states while remembering the history
		 * @param current__et - the current state
		 * @param to - the final parent state
		«IF usesHdlr»
			 * @param handler__et - entry and exit codes are called only if not handler (for handler TransitionPoints)
		«ENDIF»
		 */
		«IF generateImplementation»
			«privAccess»void «opScopePriv»exitTo(«selfPtr»«stateType» current__et, «stateType» to«IF usesHdlr», «boolType» handler__et«ENDIF») {
				while (current__et!=to) {
					switch (current__et) {
						«FOR state : states»
							case «state.getGenStateId»:
								«IF state.hasExitCode(true)»«IF usesHdlr»if (!handler__et) «ENDIF»«state.exitCodeOperationName»(«langExt.selfPointer(false)»);«ENDIF»
								«setHistory(state.parentStateId, state.genStateId)»;
								current__et = «state.parentStateId»;
								break;
						«ENDFOR»
						default:
							/* should not occur */
							break;
					}
				}
			}
		«ELSE»
			void exitTo(«selfPtr»«stateType» current__et, «stateType» to«IF usesHdlr», «boolType» handler__et«ENDIF»);
		«ENDIF»

		/**
		 * calls action, entry and exit codes along a transition chain. The generic data are cast to typed data
		 * matching the trigger of this chain. The ID of the final state is returned
		 * @param chain__et - the chain ID
		 * @param generic_data__et - the generic data pointer
		 * @return the +/- ID of the final state either with a positive sign, that indicates to execute the state's entry code, or a negative sign vice versa
		 */
		«IF generateImplementation»
			«privAccess»«stateType» «opScopePriv»executeTransitionChain(«selfPtr»int chain__et«IF handleEvents», «constIfItemPtr» ifitem, «langExt.voidPointer» generic_data__et«ENDIF») {
				switch (chain__et) {
					«FOR tc : transitionChains»
						case «chainIDScope»«tc.transition.genChainId»:
						{
							«transitionChainGenerator.generateExecuteChain(gc, tc)»
						}
					«ENDFOR»
						default:
							/* should not occur */
							break;
				}
				return NO_STATE;
			}
		«ELSE»
			«stateType» executeTransitionChain(«selfPtr»int chain__et«IF handleEvents», «constIfItemPtr» ifitem, «langExt.voidPointer» generic_data__et«ENDIF»);
		«ENDIF»

		/**
		 * calls entry codes while entering a state's history. The ID of the final leaf state is returned
		 * @param state__et - the state which is entered
		«IF usesHdlr»
			 * @param handler__et - entry code is executed if not handler
		«ENDIF»
		 * @return - the ID of the final leaf state
		 */
		«IF generateImplementation»
			«privAccess»«stateType» «opScopePriv»enterHistory(«selfPtr»«stateType» state__et«IF usesHdlr», «boolType» handler__et«ENDIF») {
				«val needsSkipVar = !states.filter(s|s.hasEntryCode(true)).empty»
				«IF needsSkipVar»
					«boolType» skip_entry__et = «langExt.booleanConstant(false)»;
				«ENDIF»
				if (state__et >= STATE_MAX) {
					state__et = «IF !langExt.usesInheritance»(«stateType»)«ENDIF» (state__et - STATE_MAX);
					«IF needsSkipVar»
						skip_entry__et = «langExt.booleanConstant(true)»;
					«ENDIF»
				}
				while («langExt.booleanConstant(true)») {
					switch (state__et) {
						«FOR state : states»
						case «state.genStateId»:
							«IF state.hasEntryCode(true)»if (!(skip_entry__et«IF usesHdlr» || handler__et«ENDIF»)) «state.entryCodeOperationName»(«langExt.selfPointer(false)»);«ENDIF»
							«IF state.isLeaf»
								/* in leaf state: return state id */
								return «state.getGenStateId»;
							«ELSE»
								/* state has a sub graph */
								«var sub_initt = state2node.get(state).subgraph.initialTransition»
								«IF sub_initt!==null»
									/* with init transition */
									if («getHistory(state.genStateId)»==NO_STATE) {
										state__et = executeTransitionChain(«langExt.selfPointer(true)»«chainIDScope»«sub_initt.genChainId»«IF handleEvents», «langExt.nullPointer», «langExt.nullPointer»«ENDIF»);
									}
									else {
										state__et = «state.genStateId.history»;
									}
								«ELSE»
									/* without init transition */
									state__et = «state.genStateId.history»;
								«ENDIF»
								break;
							«ENDIF»
						«ENDFOR»
						case STATE_TOP:
							state__et = «getHistory("STATE_TOP")»;
							break;
						default:
							/* should not occur */
							break;
					}
					«IF needsSkipVar»
						skip_entry__et = «langExt.booleanConstant(false)»;
					«ENDIF»
				}
				«unreachableReturn»
			}
		«ELSE»
			«stateType» enterHistory(«selfPtr»«stateType» state__et«IF usesHdlr», «boolType» handler__et«ENDIF»);
		«ENDIF»

		«IF generateImplementation»
			«publicIf»void «opScope»executeInitTransition(«selfOnly») {
				«var initt = gc.graph.initialTransition»
				int chain__et = «chainIDScope»«initt.genChainId»;
				«stateType» next__et = «opScopePriv»executeTransitionChain(«langExt.selfPointer(true)»chain__et«IF handleEvents», «langExt.nullPointer», «langExt.nullPointer»«ENDIF»);
				next__et = «opScopePriv»enterHistory(«langExt.selfPointer(true)»next__et«IF usesHdlr», «langExt.booleanConstant(false)»«ENDIF»);
				setState(«langExt.selfPointer(true)»next__et);
			}
		«ELSE»
			void «opScope»executeInitTransition(«selfOnly»);
		«ENDIF»

		/* receiveEvent contains the main implementation of the FSM */
		«IF generateImplementation»
			«publicIf»void «opScope»receiveEventInternal(«langExt.selfPointer(mc.className, handleEvents)»«IF handleEvents»«ifItemPtr» ifitem, int localId, int evt, «langExt.voidPointer» generic_data__et«ENDIF») {
				«IF async»
					int trigger__et = (ifitem==«langExt.nullPointer»)? POLLING : localId + EVT_SHIFT*evt;
				«ELSEIF eventDriven»
					int trigger__et = localId + EVT_SHIFT*evt;
				«ENDIF»
				int chain__et = NOT_CAUGHT;
				«stateType» catching_state__et = NO_STATE;
				«IF usesHdlr»
				«boolType» is_handler__et = «langExt.booleanConstant(false)»;
				«ENDIF»
				«IF async || eventDriven»
					«markVariableUsed("trigger__et")»
				«ENDIF»

				«IF handleEvents»
					if (!handleSystemEvent(ifitem, evt, generic_data__et)) {
						«genStateSwitch(gc, usesHdlr)»
					}
				«ELSE»
					«genStateSwitch(gc, usesHdlr)»
				«ENDIF»
				if (chain__et != NOT_CAUGHT) {
					«opScopePriv»exitTo(«langExt.selfPointer(true)»getState(«langExt.selfPointer(false)»), catching_state__et«IF usesHdlr», is_handler__et«ENDIF»);
					{
						«stateType» next__et = «opScopePriv»executeTransitionChain(«langExt.selfPointer(true)»chain__et«IF handleEvents», ifitem, generic_data__et«ENDIF»);
						next__et = «opScopePriv»enterHistory(«langExt.selfPointer(true)»next__et«IF usesHdlr», is_handler__et«ENDIF»);
						setState(«langExt.selfPointer(true)»next__et);
						«finalAction»
					}
				}
			}
		«ELSE»
			void «opScope»receiveEventInternal(«langExt.selfPointer(mc.className, handleEvents)»«IF handleEvents»«ifItemPtr» ifitem, int localId, int evt, «langExt.voidPointer» generic_data__et«ENDIF»);
		«ENDIF»
		«IF handleEvents»
			«IF generateImplementation»
				«publicIf»void «opScope»receiveEvent(«langExt.selfPointer(mc.className, true)»«ifItemPtr» ifitem, int evt, «langExt.voidPointer» generic_data__et) {
					int localId = (ifitem==«langExt.nullPointer»)? 0 : ifitem«getLocalId»;
					«opScope»receiveEventInternal(«langExt.selfPointer(true)»ifitem, localId, evt, generic_data__et);
				}
			«ELSE»
				void «opScope»receiveEvent(«langExt.selfPointer(true)»«ifItemPtr» ifitem, int evt, «langExt.voidPointer» generic_data__et);
			«ENDIF»
		«ENDIF»
		'''
	}

	/**
	 * generate the do code calls for a given state
	 *
	 * @param state the {@link State}
	 * @return the generated code
	 */
	def String genDoCodes(State state) {'''
		«IF state.hasDoCode(true)»
			«state.doCodeOperationName»(«langExt.selfPointer(false)»);
		«ENDIF»
		«IF state.eContainer.eContainer instanceof State»
			«genDoCodes(state.eContainer.eContainer as State)»
		«ENDIF»
	'''}

	/**
	 * helper method which generates the state switch.
	 * Asynchronous, data driven and event driven state machines are distinguished
	 *
	 * @param gc the {@link GraphContainer}
	 * @param usesHdlr if the state machine uses no handler {@link TransitionPoint}s
	 *	  at all then unused variables can be avoided by passing <code>true</code>
	 * @return the generated code
	 */
	def genStateSwitch(GraphContainer gc, boolean usesHdlr) {
		var async = gc.component.commType==ComponentCommunicationType::ASYNCHRONOUS
		var eventDriven = gc.component.commType==ComponentCommunicationType::EVENT_DRIVEN
		var dataDriven = gc.component.commType==ComponentCommunicationType::DATA_DRIVEN
		val allLeafStateNodes = gc.graph.allStateNodes.filter[isLeaf].toList.sortBy[(stateGraphNode as State).genStateId]
		'''
			switch (getState(«langExt.selfPointer(false)»)) {
				«FOR stateNode : allLeafStateNodes»
					«val state = stateNode.stateGraphNode as State»
					case «state.genStateId»:
						«val caughtTriggers = stateNode.caughtTriggers»
						«IF async»
							«IF !caughtTriggers.isEmpty»
								switch(trigger__et) {
									case POLLING:
										«genDataDrivenTriggers(gc, stateNode, usesHdlr)»
										break;
									«genEventDrivenTriggers(gc, stateNode, usesHdlr)»
								}
							«ELSE»
								«genDataDrivenTriggers(gc, stateNode, usesHdlr)»
							«ENDIF»
						«ELSEIF dataDriven»
								«genDataDrivenTriggers(gc, stateNode, usesHdlr)»
						«ELSEIF eventDriven»
							«IF !caughtTriggers.isEmpty»
								switch(trigger__et) {
									«genEventDrivenTriggers(gc, stateNode, usesHdlr)»
								}
							«ENDIF»
						«ENDIF»
						break;
				«ENDFOR»
				default:
					/* should not occur */
					break;
			}
		'''
	}

	/**
	 * helper method which generates the data driven triggers
	 *
	 * @param gc the {@link GraphContainer}
	 * @param state the {@link State} for which the trigger if-else switch should be generated
	 * @param usesHdlr if the state machine uses no handler {@link TransitionPoints}
	 *	  at all then unused variables can be avoided by passing <code>true</code>
	 * @return the generated code
	 */
	def genDataDrivenTriggers(GraphContainer gc, Node stateNode, boolean usesHdlr) {
		val chainIDScope = if (langExt.usesInheritance) gc.className+langExt.scopeSeparator else ""
		val state = stateNode.stateGraphNode as State
		'''
			«genDoCodes(state)»
			«var links = stateNode.getOutgoingLinksHierarchically.filter[transition instanceof GuardedTransition]»
			«FOR l : links»
				if («genGuardedTransitionGuard(l, "", gc)»)
				{
					chain__et = «chainIDScope»«l.transition.genChainId»;
					catching_state__et = «l.transition.superState.genStateId»;
					«IF l.isHandler && usesHdlr»
						is_handler__et = TRUE;
					«ENDIF»
				}
				«IF l!=links.last»
					else
				«ENDIF»
			«ENDFOR»
		'''
	}

	/**
	 * helper method which generates the event driven triggers
	 *
	 * @param gc the {@link GraphContainer}
	 * @param state the {@link State} for which the trigger switch should be generated
	 * @param usesHdlr if the state machine uses no handler {@link TransitionPoints}
	 *	  at all then unused variables can be avoided by passing <code>true</code>
	 * @return the generated code
	 */
	def genEventDrivenTriggers(GraphContainer gc, Node stateNode, boolean usesHdlr) {
		val caughtTriggers = new ArrayList(stateNode.caughtTriggers).sortBy[triggerCodeName]
		val chainIDScope = if (langExt.usesInheritance) gc.className+langExt.scopeSeparator else ""
		'''
			«FOR ct : caughtTriggers»
				case «ct.triggerCodeName»:
					«var needData = ct.hasGuard»
					«IF needData»{ «langExt.getTypedDataDefinition(ct.msg)»«ENDIF»
					«FOR link : ct.links SEPARATOR " else "»
						«genTriggeredTransitionGuard(link, ct.trigger, gc)»
						{
							chain__et = «chainIDScope»«link.transition.genChainId»;
							catching_state__et = «link.transition.superState.genStateId»;
							«IF link.isHandler && usesHdlr»
								is_handler__et = «langExt.booleanConstant(true)»;
							«ENDIF»
						}
					«ENDFOR»
					«IF needData»}«ENDIF»
				break;
			«ENDFOR»
			default:
				/* should not occur */
				break;
		'''
	}

	def getClassName(GraphContainer gc) {
		gc.component.className
	}

	def getClassName(ModelComponent mc) {
		mc.componentName
	}

	def getTriggerCodeName(CommonTrigger tr) {
		val parts = tr.trigger.split(TriggerExtensions.TRIGGER_SEP)
		return "TRIG_"+parts.get(0)+"__"+parts.get(1)
	}
	
	/**
	 * getter for history array
	 *
	 * @param state the ID of the history state
	 * @return the generated code
	 */
	def getHistory(String state) {
		langExt.memberAccess+"history["+state+"]"
	}

	/**
	 * setter for history array
	 *
	 * @param state the ID of the state whose history should be set
	 * @param historyState the ID of the state that should be assigned
	 * @return the generated code
	 */
	def setHistory(String state, String historyState) {
		langExt.memberAccess+"history["+state+"] = "+historyState
	}

	/**
	 * @return the type of (temporary) state variables (defaults to "int")
	 * and has to be signed
	 */
	def stateType() {
		"int"
	}

	/**
	 * allow target language dependent generation of unreachable return in generated enterHistory method.
	 * The default is just a comment.
	 * @return the generated code
	 */
	def unreachableReturn() {
		"/* return NO_STATE; // required by CDT but detected as unreachable by JDT because of while (true) */"
	}

	/**
	 * type of (temporary) boolean variables (defaults to "boolean")
	 * @return the generated code
	 */
	def boolType() {
		return "boolean"
	}

	/**
	 * empty, but may be overridden
	 */
	def finalAction() {
		''''''
	}

	/**
	 * the type of the interface item passed into the receiveEvent() method
	 */
	def interfaceItemType() {
		"InterfaceItemBase"
	}

	/**
	 * empty, but may be overridden
	 */
	def markVariableUsed(String varname) {
		''''''
	}

	/**
	 * helper method to determine whether this state machine uses handler transitions
	 * points at all
	 *
	 * @param xpax the {@link GraphContainer}
	 * @return <code>true</code> if the state machine uses handler transition points
	 */
	def usesHandlerTrPoints(GraphContainer gc) {
		!gc.graph.allTransitionPointNodes.filter(t|((t.stateGraphNode as TransitionPoint).handler)).empty
	}

	def String genTriggeredTransitionGuard(Link link, String trigger, GraphContainer mc)
	def String genGuardedTransitionGuard(Link link, String trigger, GraphContainer mc)
	def String genActionCodeMethod(GraphContainer gc, Link link, boolean generateImplementation)
	def String genActionCodeMethods(GraphContainer gc, Node node, boolean generateImplementation)
}

Back to the top