Fsm_From_Dia.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. /**
  3. * Input converter for FSM from Dia
  4. *
  5. * This class converts an UML diagram from Dia 0.9.6
  6. * into an abstract FSM graph which can then be output by another class
  7. * to the FSM XML format used by the OSInet FSM 1.6.
  8. *
  9. * @copyright 2007 Ouest Systèmes Informatiques
  10. * @author Frederic G. MARAND
  11. * @license CeCILL 2.0
  12. * @version CVS: $Id: Fsm_From_Dia.php,v 1.1 2007-06-10 19:37:09 marand Exp $
  13. * @link http://wiki.audean.com/fsm/fsm
  14. * @since FSM 1.6
  15. * @package fsm
  16. * @subpackage fsm.ui
  17. */
  18. $erFsmFromDia = error_reporting(E_ALL|E_STRICT);
  19. /**
  20. * This class converts an UML diagram from Dia 0.9.6
  21. * into an abstract FSM graph.
  22. *
  23. * @todo Validate the diagram: currently it will just choke on non-accepted diagrams
  24. */
  25. class Fsm_From_Dia
  26. {
  27. const DIA_NAMESPACE = "http://www.lysator.liu.se/~alla/dia/";
  28. /**
  29. * The DOM for the source Dia diagram
  30. *
  31. * @var DOMDocument
  32. */
  33. protected $dom;
  34. /**
  35. * Extract the initial/final status from a Dia "UML - State Term"
  36. *
  37. * @param DOMElement $element
  38. * @return array
  39. */
  40. protected function getStateTermInfo($element)
  41. {
  42. $query = 'dia:attribute[@name="is_final"]/dia:boolean';
  43. $xpath = new DOMXPath($this->dom);
  44. $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
  45. $id = $element->getAttribute('id');
  46. $dia_attributes = $xpath->query($query, $element);
  47. if ($dia_attributes->length == 1) // Normal case
  48. {
  49. $dia_boolean = $dia_attributes->item(0); // "is_final" is a Dia boolean
  50. $val = $dia_boolean->getAttribute('val');
  51. switch ($val)
  52. {
  53. case 'true': $ret = 'final'; break;
  54. case 'false': $ret = 'initial'; break;
  55. default: $ret = "anomalous($val)"; break;
  56. }
  57. }
  58. else
  59. {
  60. echo "Initial/final state #$id does not bear the is_final attribute: anomaly.\n";
  61. }
  62. return array('type' => $ret, 'name' => $ret);
  63. }
  64. /**
  65. * Extract the name from a Dia "UML - State"
  66. *
  67. * @param DOMElement $element
  68. * @return array
  69. */
  70. protected function getStateInfo($element)
  71. {
  72. $query = 'dia:attribute[@name="text"]/dia:composite/dia:attribute[@name="string"]/dia:string';
  73. $xpath = new DOMXPath($this->dom);
  74. $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
  75. $id = $element->getAttribute('id');
  76. $dia_string = $xpath->query($query, $element);
  77. if ($dia_string->length == 1) // Normal case
  78. {
  79. $dia_text = $dia_string->item(0);
  80. $ret = trim($dia_text->textContent, '#');
  81. }
  82. else
  83. {
  84. echo "Standard state #$id does not contain the expected content: anomaly.\n";
  85. }
  86. return array('type' => 'standard', 'name' => $ret);
  87. }
  88. /**
  89. * Extract the actual text content from a query for a Dia string element.
  90. * This is to be used over XPath query results
  91. *
  92. * @param DOMNodeList $nodes
  93. * @return string
  94. */
  95. private function getTextFromDiaString ($nodes)
  96. {
  97. if ($nodes->length == 1)
  98. {
  99. $ret = $nodes->item(0);
  100. $ret = trim($ret->textContent, '#');
  101. }
  102. else
  103. {
  104. $ret = NULL;
  105. }
  106. return $ret;
  107. }
  108. /**
  109. * Extract the various information fields from a Dia "UML - Connection"
  110. *
  111. * @param DOMElement $element
  112. * @return array
  113. */
  114. protected function getTransitionInfo($element)
  115. {
  116. $xpath = new DOMXPath($this->dom);
  117. $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE );
  118. $id = $element->getAttribute('id');
  119. $baseQuery = 'dia:attribute[@name="%s"]/dia:string';
  120. foreach (array('trigger', 'action', 'guard') as $parameter)
  121. {
  122. $query = sprintf($baseQuery, $parameter);
  123. $nodes = $xpath->query($query, $element);
  124. $ret[$parameter] = $this->getTextFromDiaString($nodes);
  125. if ($parameter <> 'guard') // triggers and actions
  126. {
  127. $ret[$parameter] = ucfirst($ret[$parameter]);
  128. }
  129. }
  130. $query = 'dia:connections/dia:connection';
  131. $dia_connections = $xpath->query($query, $element);
  132. // echo "Connections: $dia_connections->length\n";
  133. if ($dia_connections->length == 2)
  134. {
  135. // echo "Transition $id links" ;
  136. foreach ($dia_connections as $end)
  137. {
  138. $handle = $end->getAttribute('handle');
  139. $link = $end->getAttribute('to');
  140. switch ($handle)
  141. {
  142. case '0' : $kind = 'from'; $ret['from'] = $link ; break;
  143. case '1' : $kind = 'to'; $ret['to'] = $link ; break;
  144. default: $kind = "anomaly($handle)"; break;
  145. }
  146. // echo " $kind $link";
  147. }
  148. }
  149. else
  150. {
  151. echo "Anomaly detected on the connection properties of transiition #$id\n";
  152. }
  153. // echo " on ${trigger}[$guard]/$action.\n";
  154. return $ret;
  155. }
  156. /**
  157. * Load a Dia file into the DOM
  158. *
  159. * @param string $filePath
  160. * @return void
  161. */
  162. public function __construct($filePath)
  163. {
  164. $this->dom = new DOMDocument();
  165. $this->dom->load($filePath);
  166. }
  167. /**
  168. * Parse the DOM to extract the various UML elements to an abstract FSM array
  169. * @return array
  170. */
  171. public function parse()
  172. {
  173. $query = '//dia:object';
  174. $xpath = new DOMXPath($this->dom);
  175. $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
  176. $result = $xpath->query($query);
  177. foreach ($result as $object)
  178. {
  179. $type = $object->getAttribute('type');
  180. $id = $object->getAttribute('id');
  181. switch ($type)
  182. {
  183. case 'UML - State Term':
  184. $states[$id] = $this->getStateTermInfo($object);
  185. break;
  186. case 'UML - State':
  187. $states[$id] = $this->getStateInfo($object);
  188. break;
  189. case 'UML - Transition':
  190. $transitions[$id] = $this->getTransitionInfo($object);
  191. break;
  192. default:
  193. echo "Object #$id is of unknown type $type: ignored.\n";
  194. break;
  195. }
  196. }
  197. return array('states' => $states, 'transitions' => $transitions);
  198. }
  199. }
  200. error_reporting($erFsmFromDia);
  201. unset ($erFsmFromDia);