/*
 * Finite State Machine using JAXP (Sun Java API for XML Parsing 1.0)
 */

import java.io.*;
import java.util.*;

import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.xml.sax.*;

public class fsm {

 /*
  * elementからattribute_nameなるアトリビュート値を取り出す
  */
  private static String get_attribute(Element element, String attribute_name) {
    return element.getAttribute(attribute_name);
  }

 /*
  * parentの子nodeの中からattribute_nameなるアトリビュート値がkeyに一致するものを見つける
  */
  private static Element find_element(Node parent, String attribute_name, String key) {
    Element element = null;
    for ( Node node = parent.getFirstChild(); node != null; node = node.getNextSibling() ) {
      if ( node.getNodeType() == Node.ELEMENT_NODE ) {
        element = (Element)node;
        if ( get_attribute(element,attribute_name).equals(key) )
          return element;
      }
    }
    return element;
  }

 /*
  * ----- main -----
  */
  public static void main(String argv []) {

          if (argv.length != 1) {
            System.err.println("fsm <filename>");
            System.exit(1);
          }

    Document document;

          try {

            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder        docBuilder        = docBuilderFactory.newDocumentBuilder();

      docBuilderFactory.setValidating(true);

     /*
      * ドキュメントを取得する
      */
            document = docBuilder.parse(new File(argv[0]));

     /*
      * ルートエレメントを取得する
      */
      Element root = document.getDocumentElement();

     /*
      * 全イベントを取り出す
      */

      TreeSet event_set = new TreeSet();
      NodeList events = root.getElementsByTagName("Event");
      for ( int i = 0; i < events.getLength(); ++i ) {
        Element node = (Element)events.item(i);
        event_set.add(get_attribute(node,"name"));
      }

     /*
      * 初期状態 / 終了状態 を取得する
      */
      String state_name     = get_attribute(root,"initial");
      String terminal_name  = get_attribute(root,"terminal");

      String fsm_name       = get_attribute(root,"name");
      System.out.println("FSM : " + fsm_name);

     /*
      * 状態遷移表を駆動する
      */
      while ( !state_name.equals(terminal_name) ) {

       /*
        * state_name で示される State を見つける
        */
        Element state  = find_element(root,"name",state_name);
        if ( state == null ) {
           System.err.println("can't find state : " + state_name);
           break;
        }

       /*
        * 現在の状態とイベント一覧を出力する
        */
        System.out.println("state : " + state_name);
        System.out.print("enter an event (");
        Iterator it = event_set.iterator();
        while ( it.hasNext() ) {
          System.out.print(it.next() + ",");
        }
        System.out.print("exit) ? ");

       /*
        * ユーザからのイベント入力
        */
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        String event_name = input.readLine();
        if ( event_name.equals("exit") )
          break;
        if ( !event_set.contains(event_name) ) {
          System.err.println(event_name + " is not valid.");
          continue;
        }

       /*
        * event_name で示される Event を見つける
        */
        Element event = find_element(state,"name",event_name);
        if ( event == null ) {
          System.err.println("can't find event : " + event_name);
          break;
       }

      /*
       * アクションを実行する
       */
       String action_name = get_attribute(event,"action");
       if ( !action_name.equals("none") )
         System.out.println("action : " + action_name);

      /*
       * 次の状態へ遷移する
       */
       String transition_name = get_attribute(event,"transition");
       if ( !transition_name.equals("none") )
         state_name = transition_name;


     }

    } catch ( SAXParseException err ) {
            System.out.println("** Parsing error"
                         + ", line " + err.getLineNumber ()
                         + ", uri " + err.getSystemId ());
      System.out.println("   " + err.getMessage ());
    } catch ( SAXException e ) {
      Exception x = e.getException();
      ((x == null) ? e : x).printStackTrace ();
    } catch (Throwable t) {
      t.printStackTrace ();
          }
  }

}

