[Fact] public void HierarchyOutput() { var node4 = new RankedNode(); var node5 = new RankedNode(node4); var node6 = new ValueNode <int>(node5, 9); var node7 = new ValueNode <int>(node5, 7); var node1 = new RankedNode(node4); var node2 = new ValueNode <int>(node1, 6); var node3 = new ValueNode <int>(node1, 3); var node8 = new RankedNode(node4); var node9 = new ValueNode <int>(node8, 5); var node10 = new ValueNode <int>(node8, 2); var hier = new Hierarchy <int>(node4); // T.FileWriter.SetDirAndFile("Tests", nameof(hier), ".txt"); // T.FileWriter.WriteLine(hier); hier.MakeTopNode(node5); // T.FileWriter.WriteLine(hier); hier.MakeTopNode(node4); // T.FileWriter.WriteLine(hier); // T.FileWriter.Flush(); var strw = new StringWriter(); strw.Write <int>(hier); Assert.True(strw.ToString() == "{{9, 7}, {6, 3}, {5, 2}}"); }
/// <summary>Attempts to read a data stream fed by a text reader as a hierarchy. Throws an exception if the data is not convertible to a hierarchy.</summary> /// <param name="tr">Text reader such as FileReader.</param> /// <typeparam name="τ">Type of Hierarchy elements.</typeparam> public static Hierarchy <τ>?ReadHierarchy <τ>(this TextReader tr) where τ : IEquatable <τ>, new() { var nFI = new NumberFormatInfo(); nFI.NumberDecimalSeparator = "."; var types = new Type[] { typeof(string), typeof(IFormatProvider) }; var parseMethod = typeof(τ).GetMethod("Parse", types); if (parseMethod == null) { throw new NullReferenceException("Parse method not found."); } var parse = (Func <string, IFormatProvider, τ>)Delegate.CreateDelegate( typeof(Func <string, IFormatProvider, τ>), parseMethod); var wholeStr = tr.ReadToEnd(); var sb = new StringBuilder(@"(\w+\.?\w*|", 300); sb.Append(@"\{ "); sb.Append(@" (?> "); sb.Append(@" [^{}]+ "); sb.Append(@" | "); sb.Append(@" \{ (?<DEPTH>) "); sb.Append(@" | "); sb.Append(@" } (?<-DEPTH>) "); sb.Append(@" )* "); sb.Append(@" (?(DEPTH)(?!)) "); sb.Append(@"} "); sb.Append(@")"); // The end of an alternator from first line. var matcher = new Regex(sb.ToString(), RegexOptions.IgnorePatternWhitespace, new TimeSpan(0, 0, 1)); var firstMatch = matcher.Match(wholeStr); if (firstMatch.Success) // Now check elements inside it. { var topNode = new RankedNode(); var hier = new Hierarchy <τ>(topNode); Recursion(firstMatch.Value, topNode); return(hier); } else { return(null); } void Recursion(string prevMatched, RankedNode nodeAbove) { var sb2 = new StringBuilder(prevMatched, 300); sb2.Remove(prevMatched.Length - 1, 1).Remove(0, 1); // Strip it of outer braces. prevMatched = sb2.ToString(); // Now it is without outer braces. Match newMatch = matcher.Match(prevMatched); // Match either first value or braces while (newMatch.Success) // If there was anything inside at all. { if (newMatch.Value[0] == '{') // If it was a non-value node. { Recursion(newMatch.Value, new RankedNode(nodeAbove)); } else // If it was a value node. { new ValueNode <τ>(nodeAbove, parse(newMatch.Value, nFI)); } newMatch = newMatch.NextMatch(); } } }