/// <summary> /// Write the automaton in dgml format. /// </summary> /// <param name="fa">the automaton to write</param> /// <param name="tw">text writer for the output</param> public static void AutomatonToDgml <S>(int k, IAutomaton <S> fa, string name, System.IO.TextWriter tw, Func <S, string> describeS = null) { ITransducer <S> faf = fa as ITransducer <S>; bool isTransducer = (faf != null); Func <S, bool> isfinal = lab => { return(isTransducer ? faf.IsFinalRule(lab) : false); }; var finalMoves = new Dictionary <int, List <S> >(); var nonFinalMoves = new Dictionary <Tuple <int, int>, List <S> >(); var epsilonmoves = new List <Move <S> >(); var nonEpsilonStates = new HashSet <int>(); Func <int, bool> IsEpsilonState = (s => !nonEpsilonStates.Contains(s)); foreach (var move in fa.GetMoves()) { if (move.IsEpsilon) { epsilonmoves.Add(move); } else if (isfinal(move.Label) && !(faf.IsGuardTrue(move.Label) && faf.GetYieldsLength(move.Label) == 0)) { List <S> rules; if (!finalMoves.TryGetValue(move.SourceState, out rules)) { rules = new List <S>(); finalMoves[move.SourceState] = rules; } rules.Add(move.Label); } else if (!isfinal(move.Label)) { nonEpsilonStates.Add(move.SourceState); List <S> rules; var p = new Tuple <int, int>(move.SourceState, move.TargetState); if (!nonFinalMoves.TryGetValue(p, out rules)) { rules = new List <S>(); nonFinalMoves[p] = rules; } rules.Add(move.Label); } } tw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); tw.WriteLine("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\" ZoomLevel=\"1.5\" GraphDirection=\"TopToBottom\" >"); tw.WriteLine("<Nodes>"); tw.WriteLine("<Node Id=\"init\" Label=\"{0}\" Stroke=\"white\" Background=\"white\"/>", name); foreach (int state in fa.GetStates()) { if (state == fa.InitialState && fa.IsFinalState(state)) { tw.WriteLine("<Node Id=\"{0}\" Label=\"{1}\" Category=\"State\" >", state, fa.DescribeState(state)); if (!finalMoves.ContainsKey(state)) { //if (IsEpsilonState(state)) // tw.WriteLine("<Category Ref=\"EpsilonState\" />"); //else tw.WriteLine("<Category Ref=\"FinalState\" />"); } //tw.WriteLine("<Category Ref=\"InitialState\" />"); tw.WriteLine("</Node>"); if (finalMoves.ContainsKey(state)) { tw.WriteLine("<Node Id=\"f{0}\" Label=\" \" Category=\"State\" >", state); tw.WriteLine("<Category Ref=\"FinalState\" />"); tw.WriteLine("<Category Ref=\"SinkState\" />"); tw.WriteLine("</Node>"); } } else if (state == fa.InitialState) { tw.WriteLine("<Node Id=\"{0}\" Label=\"{1}\" Category=\"State\" >", state, fa.DescribeState(state)); tw.WriteLine("<Category Ref=\"InitialState\" />"); tw.WriteLine("</Node>"); } else if (fa.IsFinalState(state)) { tw.WriteLine("<Node Id=\"{0}\" Label=\"{1}\" Category=\"State\" >", state, fa.DescribeState(state)); if (!finalMoves.ContainsKey(state)) { //if (IsEpsilonState(state)) // tw.WriteLine("<Category Ref=\"EpsilonState\" />"); //else tw.WriteLine("<Category Ref=\"FinalState\" />"); } tw.WriteLine("</Node>"); if (finalMoves.ContainsKey(state)) { tw.WriteLine("<Node Id=\"f{0}\" Label=\" \" Category=\"State\" >", state); tw.WriteLine("<Category Ref=\"FinalState\" />"); tw.WriteLine("<Category Ref=\"SinkState\" />"); tw.WriteLine("</Node>"); } } else { tw.WriteLine("<Node Id=\"{0}\" Label=\"{1}\" Category=\"State\" />", state, fa.DescribeState(state)); } } tw.WriteLine("</Nodes>"); tw.WriteLine("<Links>"); tw.WriteLine("<Link Source=\"init\" Target=\"{0}\" Label=\"{1}\" Category=\"StartTransition\" />", fa.InitialState, fa.DescribeStartLabel()); foreach (var move in epsilonmoves) { tw.WriteLine("<Link Source=\"{0}\" Target=\"{1}\" Category=\"EpsilonTransition\" />", move.SourceState, move.TargetState); } foreach (var move in nonFinalMoves) { tw.WriteLine(GetNonFinalRuleInfo(k, fa, faf, move.Key.Item1, move.Key.Item2, move.Value, describeS)); } foreach (var move in finalMoves) { tw.WriteLine(GetFinalRuleInfo(k, faf, move.Key, move.Value)); } tw.WriteLine("</Links>"); WriteCategoriesAndStyles(tw); tw.WriteLine("</DirectedGraph>"); }