StateChartNode.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Xml.Linq;
  5. namespace csharp_sccd_compiler
  6. {
  7. public class StateChartNode : Visitable
  8. {
  9. public bool is_basic { get; private set; }
  10. /// <summary>
  11. /// Gets a value indicating whether this <see cref="csharp_sccd_compiler.StateChartNode"/> is a parallel state.
  12. /// </summary>
  13. /// <value><c>true</c> if is_parallel; otherwise, <c>false</c>.</value>
  14. public bool is_parallel { get; private set; }
  15. /// <summary>
  16. /// Gets a value indicating whether this <see cref="csharp_sccd_compiler.StateChartNode"/> is a composite state.
  17. /// </summary>
  18. /// <value><c>true</c> if is_composite; otherwise, <c>false</c>.</value>
  19. public bool is_composite { get; private set; }
  20. public bool is_history { get; private set; }
  21. /// <summary>
  22. /// Gets a value indicating whether this <see cref="csharp_sccd_compiler.StateChartNode"/> is history state of type <c>deep</c>.
  23. /// </summary>
  24. /// <value><c>true</c> if is_history_deep; otherwise, <c>false</c>.</value>
  25. public bool is_history_deep { get; private set; }
  26. public bool is_root { get; private set; }
  27. public bool save_state_on_exit { get; set; }
  28. public bool solves_conflict_outer { get; private set; }
  29. public string name { get; private set; }
  30. public string full_name { get; private set; }
  31. /// <summary>
  32. /// The children nodes of this node.
  33. /// </summary>
  34. public List<StateChartNode> children { get; private set; }
  35. /// <summary>
  36. /// The parent node of this node. In case of a root node, this property is <c>null</c>.
  37. /// </summary>
  38. public StateChartNode parent { get; private set; }
  39. public List<StateChartTransition> transitions { get; private set; }
  40. public EnterAction enter_action { get; private set; }
  41. public ExitAction exit_action { get; private set; }
  42. public List<StateChartNode> defaults { get; private set; }
  43. /// <summary>
  44. /// Initializes a new instance of the <see cref="csharp_sccd_compiler.StateChartNode"/> class.
  45. /// </summary>
  46. /// <param name="xml">The XML element that contains the information related to this node.</param>
  47. /// <param name="type">The type of node.</param>
  48. /// <param name="is_orthogonal">If set to <c>true</c> this node is orthogonal. (= child of a parallel state.</param>
  49. /// <param name="parent">The parent of this node. Defaults to null, which means the node is the root.</param>
  50. public StateChartNode(XElement xml, StateChartNode parent = null)
  51. {
  52. this.parent = parent;
  53. this.children = new List<StateChartNode>();
  54. this.is_root = false;
  55. this.is_basic = false;
  56. this.is_composite = false;
  57. this.is_history = false;
  58. this.is_history_deep = false;
  59. this.is_parallel = false;
  60. this.save_state_on_exit = false;
  61. if (xml.Name == "scxml")
  62. {
  63. this.is_root = true;
  64. this.is_composite = true;
  65. }
  66. else if (xml.Name == "parallel")
  67. {
  68. this.is_composite = true;
  69. this.is_parallel = true;
  70. }
  71. else if (xml.Name == "state")
  72. {
  73. if (xml.Element("state") != null || xml.Element("parallel") != null)
  74. this.is_composite = true;
  75. else
  76. this.is_basic = true;
  77. if (this.parent.is_parallel)
  78. {
  79. if (this.is_basic)
  80. throw new CompilerException("Orthogonal nodes (nodes that are immediate children of parallel nodes) can't be basic.");
  81. }
  82. }
  83. else if (xml.Name == "history")
  84. {
  85. this.is_history = true;
  86. XAttribute type_attribute = xml.Attribute("type");
  87. if (type_attribute != null)
  88. {
  89. string history_type = type_attribute.Value.Trim();
  90. if (history_type == "deep")
  91. this.is_history_deep = true;
  92. else if (history_type != "shallow")
  93. throw new CompilerException("Invalid history type.");
  94. }
  95. }
  96. else
  97. return;
  98. this.resolveName(xml);
  99. this.parseConflictAttribute(xml);
  100. this.parseEnterActions(xml);
  101. this.parseExitActions(xml);
  102. //Parse transitions
  103. this.transitions = new List<StateChartTransition>();
  104. foreach (XElement transition_xml in xml.Elements("transition"))
  105. this.transitions.Add(new StateChartTransition(transition_xml, this));
  106. this.optimizeTransitions();
  107. this.generateChildren(xml);
  108. this.calculateDefaults(xml);
  109. }
  110. private void resolveName(XElement xml)
  111. {
  112. if (this.is_root)
  113. {
  114. this.name = "Root";
  115. this.full_name = "Root";
  116. }
  117. else
  118. {
  119. XAttribute name_attribute = xml.Attribute("id");
  120. if (name_attribute == null)
  121. throw new CompilerException("Currently states without id aren't allowed.");
  122. this.name = name_attribute.Value.Trim();
  123. if (this.name == "")
  124. throw new CompilerException("Currently states need an non-empty id.");
  125. this.full_name = string.Format("{0}_{1}", this.parent.full_name, this.name);
  126. }
  127. }
  128. private void parseConflictAttribute(XElement xml)
  129. {
  130. XAttribute conflict_attribute = xml.Attribute("conflict");
  131. if(conflict_attribute != null)
  132. {
  133. string conflict = conflict_attribute.Value.Trim();
  134. if (conflict == "outer")
  135. {
  136. this.solves_conflict_outer = true;
  137. return;
  138. }
  139. else if (conflict == "inner")
  140. {
  141. this.solves_conflict_outer = false;
  142. return;
  143. }
  144. if (conflict != "" && conflict != "inherit")
  145. throw new CompilerException( string.Format("Unknown conflict attribute for {0}.", this.full_name));
  146. }
  147. //Do our default inherit action
  148. if (this.is_root || this.parent.solves_conflict_outer)
  149. this.solves_conflict_outer = true;
  150. else
  151. this.solves_conflict_outer = false;
  152. }
  153. private void parseEnterActions(XElement xml)
  154. {
  155. XElement onentry_xml = xml.Element("onentry");
  156. if (onentry_xml == null)
  157. this.enter_action = new EnterAction(this);
  158. else
  159. {
  160. this.enter_action = new EnterAction(this, onentry_xml);
  161. if (onentry_xml.ElementsAfterSelf("onentry").Any())
  162. throw new CompilerException(string.Format("Multiple <onentry> tags detected for {0}, only 1 allowed.", this.full_name));
  163. }
  164. }
  165. private void parseExitActions(XElement xml)
  166. {
  167. XElement onexit_xml = xml.Element("onexit");
  168. if (onexit_xml == null)
  169. this.exit_action = new ExitAction(this);
  170. else
  171. {
  172. this.exit_action = new ExitAction(this, onexit_xml);
  173. if (onexit_xml.ElementsAfterSelf("onexit").Any())
  174. throw new CompilerException(string.Format("Multiple <onexit> tags detected for {0}, only 1 allowed.", this.full_name));
  175. }
  176. }
  177. /// <summary>
  178. /// If a transition with no trigger and no guard is found then it is considered as the only transition.
  179. /// Otherwise the list is ordered by placing transitions having guards only first.
  180. /// </summary>
  181. private void optimizeTransitions()
  182. {
  183. List<StateChartTransition> with_trigger = new List<StateChartTransition>();
  184. List<StateChartTransition> only_guard = new List<StateChartTransition>();
  185. List<StateChartTransition> uc_and_no_guard = new List<StateChartTransition>();
  186. foreach( StateChartTransition transition in this.transitions)
  187. {
  188. if (transition.trigger.is_uc)
  189. {
  190. if (transition.guard == null)
  191. {
  192. if (uc_and_no_guard.Count > 0)
  193. throw new TransitionException("More than one transition found at a single node, that has no trigger and no guard.");
  194. uc_and_no_guard.Add(transition);
  195. }
  196. else
  197. {
  198. only_guard.Add(transition);
  199. }
  200. }
  201. else
  202. {
  203. with_trigger.Add(transition);
  204. }
  205. }
  206. if (uc_and_no_guard.Count > 0)
  207. this.transitions = uc_and_no_guard;
  208. else
  209. {
  210. only_guard.AddRange(with_trigger);
  211. this.transitions = only_guard;
  212. }
  213. }
  214. private void generateChildren(XElement xml)
  215. {
  216. List<string> children_names = new List<string>();
  217. foreach (XElement child_xml in xml.Elements())
  218. {
  219. StateChartNode child = new StateChartNode(child_xml, this);
  220. if (!child.is_composite && !child.is_basic && !child.is_history)
  221. continue;
  222. this.children.Add(child);
  223. //Check if the name of the child is valid
  224. if (children_names.Contains(child.name))
  225. throw new CompilerException(string.Format("Found 2 equivalent state id's '{0}' as children of state '{1}'", child.name, this.full_name));
  226. children_names.Add(child.name);
  227. }
  228. }
  229. private void calculateDefaults(XElement xml)
  230. {
  231. XAttribute initial_state_attribute = xml.Attribute("initial");
  232. string initial_state = "";
  233. if (initial_state_attribute != null)
  234. initial_state = initial_state_attribute.Value.Trim();
  235. if (this.is_parallel)
  236. {
  237. this.defaults = (from child in this.children where !child.is_history select child).ToList();
  238. if (initial_state != "")
  239. throw new CompilerException(string.Format("Component <{0}> contains an initial state while being parallel.", this.full_name));
  240. }
  241. else if (initial_state == "")
  242. {
  243. if (!this.is_basic && !this.is_history)
  244. {
  245. if (this.children.Count == 1)
  246. this.defaults = this.children;
  247. else
  248. throw new CompilerException(string.Format("Component <{0}> contains no default state.", this.full_name));
  249. }
  250. }
  251. else
  252. {
  253. if (this.is_basic)
  254. throw new CompilerException(string.Format("Component <{0}> contains a default state while being a basic state.", this.full_name));
  255. this.defaults = new List<StateChartNode>();
  256. foreach (StateChartNode child in this.children)
  257. {
  258. if (child.name == initial_state)
  259. this.defaults.Add(child);
  260. }
  261. if (this.defaults.Count < 1)
  262. throw new CompilerException(string.Format("Initial state '{0}' referred to, is missing in {1}.", initial_state, this.full_name));
  263. else if (this.defaults.Count > 1)
  264. throw new CompilerException(string.Format("Multiple states with the name '{0}' found in {1} which is referred to as initial state.", initial_state, this.full_name));
  265. }
  266. }
  267. /// <summary>
  268. /// Returns a list representing the containment hierarchy of this node.
  269. /// </summary>
  270. /// <returns>The ancestors with node being the first element and its outermost parent (root) being the last.</returns>
  271. public IEnumerable<StateChartNode> getAncestors()
  272. {
  273. StateChartNode current = this;
  274. while ( !current.is_root)
  275. {
  276. current = current.parent;
  277. yield return current;
  278. }
  279. }
  280. public bool isDescendantOf(StateChartNode anc)
  281. {
  282. StateChartNode current = this;
  283. while(! current.is_root)
  284. {
  285. current = current.parent;
  286. if (object.ReferenceEquals(current,anc))
  287. return true;
  288. }
  289. return false;
  290. }
  291. public bool isDescendantOrAncestorOf(StateChartNode node)
  292. {
  293. return this.isDescendantOf(node) || node.isDescendantOf(this);
  294. }
  295. public override void accept(Visitor visitor)
  296. {
  297. visitor.visit (this);
  298. }
  299. }
  300. }