Browse Source

First version generating a FSM as an abstract graph.

Frederic G. Marand 17 years ago
parent
commit
481937cbb7
1 changed files with 223 additions and 0 deletions
  1. 223 0
      Fsm_From_Dia.php

+ 223 - 0
Fsm_From_Dia.php

@@ -0,0 +1,223 @@
+<?php
+/**
+ * Input converter for FSM from Dia
+ *
+ * This class converts an UML diagram from Dia 0.9.6
+ * into an abstract FSM graph which can then be output by another class
+ * to the FSM XML format used by the OSInet FSM 1.6.
+ *
+ * @copyright  2007 Ouest Systèmes Informatiques
+ * @author     Frederic G. MARAND
+ * @license    CeCILL 2.0
+ * @version    CVS: $Id: Fsm_From_Dia.php,v 1.1 2007-06-10 19:37:09 marand Exp $
+ * @link       http://wiki.audean.com/fsm/fsm
+ * @since      FSM 1.6
+ * @package    fsm
+ * @subpackage fsm.ui
+*/
+
+$erFsmFromDia = error_reporting(E_ALL|E_STRICT);
+
+/**
+ * This class converts an UML diagram from Dia 0.9.6
+ * into an abstract FSM graph.
+ *
+ * @todo Validate the diagram: currently it will just choke on non-accepted diagrams
+ */
+class Fsm_From_Dia
+  {
+  const DIA_NAMESPACE = "http://www.lysator.liu.se/~alla/dia/";
+
+  /**
+   * The DOM for the source Dia diagram
+   *
+   * @var DOMDocument
+   */
+  protected $dom;
+
+  /**
+   * Extract the initial/final status from a Dia "UML - State Term"
+   *
+   * @param DOMElement $element
+   * @return array
+   */
+  protected function getStateTermInfo($element)
+    {
+    $query = 'dia:attribute[@name="is_final"]/dia:boolean';
+
+    $xpath = new DOMXPath($this->dom);
+    $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
+
+    $id = $element->getAttribute('id');
+    $dia_attributes = $xpath->query($query, $element);
+    if ($dia_attributes->length == 1) // Normal case
+      {
+      $dia_boolean = $dia_attributes->item(0); // "is_final" is a Dia boolean
+      $val = $dia_boolean->getAttribute('val');
+      switch ($val)
+        {
+        case 'true':  $ret = 'final';   break;
+        case 'false': $ret = 'initial'; break;
+        default:      $ret = "anomalous($val)"; break;
+        }
+      }
+    else
+      {
+      echo "Initial/final state #$id does not bear the is_final attribute: anomaly.\n";
+      }
+
+    return array('type' => $ret, 'name' => $ret);
+    }
+
+  /**
+   * Extract the name from a Dia "UML - State"
+   *
+   * @param DOMElement $element
+   * @return array
+   */
+  protected function getStateInfo($element)
+    {
+    $query = 'dia:attribute[@name="text"]/dia:composite/dia:attribute[@name="string"]/dia:string';
+    $xpath = new DOMXPath($this->dom);
+    $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
+
+    $id = $element->getAttribute('id');
+    $dia_string = $xpath->query($query, $element);
+    if ($dia_string->length == 1) // Normal case
+      {
+      $dia_text = $dia_string->item(0);
+      $ret = trim($dia_text->textContent, '#');
+      }
+    else
+      {
+      echo "Standard state #$id does not contain the expected content: anomaly.\n";
+      }
+    return array('type' => 'standard', 'name' => $ret);
+    }
+
+
+  /**
+   * Extract the actual text content from a query for a Dia string element.
+   * This is to be used over XPath query results
+   *
+   * @param DOMNodeList $nodes
+   * @return string
+   */
+  private function getTextFromDiaString ($nodes)
+    {
+    if ($nodes->length == 1)
+      {
+      $ret = $nodes->item(0);
+      $ret = trim($ret->textContent, '#');
+      }
+    else
+      {
+      $ret = NULL;
+      }
+    return $ret;
+    }
+
+  /**
+   * Extract the various information fields from a Dia "UML - Connection"
+   *
+   * @param DOMElement $element
+   * @return array
+   */
+  protected function getTransitionInfo($element)
+    {
+    $xpath = new DOMXPath($this->dom);
+    $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE );
+    $id = $element->getAttribute('id');
+
+    $baseQuery = 'dia:attribute[@name="%s"]/dia:string';
+    foreach (array('trigger', 'action', 'guard') as $parameter)
+      {
+      $query = sprintf($baseQuery, $parameter);
+      $nodes = $xpath->query($query, $element);
+      $ret[$parameter] = $this->getTextFromDiaString($nodes);
+      if ($parameter <> 'guard') // triggers and actions
+        {
+        $ret[$parameter] = ucfirst($ret[$parameter]);
+        }
+      }
+
+    $query = 'dia:connections/dia:connection';
+    $dia_connections = $xpath->query($query, $element);
+    // echo "Connections: $dia_connections->length\n";
+    if ($dia_connections->length == 2)
+      {
+      // echo "Transition $id links" ;
+      foreach ($dia_connections as $end)
+        {
+        $handle = $end->getAttribute('handle');
+        $link   = $end->getAttribute('to');
+        switch ($handle)
+          {
+          case '0' : $kind = 'from'; $ret['from'] = $link ; break;
+          case '1' : $kind = 'to';   $ret['to']   = $link ; break;
+          default:   $kind = "anomaly($handle)"; break;
+          }
+        // echo " $kind $link";
+        }
+      }
+    else
+      {
+      echo "Anomaly detected on the connection properties of transiition #$id\n";
+      }
+    // echo " on ${trigger}[$guard]/$action.\n";
+    return $ret;
+    }
+
+  /**
+   * Load a Dia file into the DOM
+   *
+   * @param string $filePath
+   * @return void
+   */
+  public function __construct($filePath)
+    {
+    $this->dom = new DOMDocument();
+    $this->dom->load($filePath);
+    }
+
+  /**
+   * Parse the DOM to extract the various UML elements to an abstract FSM array
+   * @return array
+   */
+  public function parse()
+    {
+    $query = '//dia:object';
+    $xpath = new DOMXPath($this->dom);
+    $xpath->registerNamespace("dia", Fsm_From_Dia::DIA_NAMESPACE);
+    $result = $xpath->query($query);
+
+    foreach ($result as $object)
+      {
+      $type = $object->getAttribute('type');
+      $id   = $object->getAttribute('id');
+      switch ($type)
+        {
+        case 'UML - State Term':
+          $states[$id] = $this->getStateTermInfo($object);
+          break;
+
+        case 'UML - State':
+          $states[$id] = $this->getStateInfo($object);
+          break;
+
+        case 'UML - Transition':
+          $transitions[$id] = $this->getTransitionInfo($object);
+          break;
+
+        default:
+          echo "Object #$id is of unknown type $type: ignored.\n";
+          break;
+        }
+      }
+    return array('states' => $states, 'transitions' => $transitions);
+    }
+  }
+
+
+error_reporting($erFsmFromDia);
+unset ($erFsmFromDia);