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);