using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using VisualTraceRoute.Net; namespace VisualTraceRoute.Text { /// /// Text parser for writing output to various data structures. /// public class Parser { private List _blocks; private FileInfo _templateFile; /// /// Initializes a new instance of the Parser class. /// public Parser() { this._blocks = new List(); this._templateFile = null; } /// /// Initializes a new instance of the Parser class. /// /// Template file path. public Parser(string TemplateFile) : this(new FileInfo(TemplateFile)) { // Stubble. } /// /// Initializes a new instance of the Parser class. /// /// Template file. public Parser(FileInfo TemplateFile) { this._blocks = new List(); this._templateFile = TemplateFile; } /// /// Read the associated template. /// public void ReadTemplate() { // Clear previous text blocks stored. this._blocks.Clear(); if (this._templateFile == null) { return; } if (this._templateFile.Exists) { using (TextReader reader = new StreamReader(this._templateFile.OpenRead())) { StringBuilder newBlock = new StringBuilder(); // Check if there is more content to read. while (reader.Peek() >= 0) { // Read the latest line. string line = reader.ReadLine(); // Determine if there are any tokens that need to be processed. if (line.ToUpper().Contains("{ROUTES}")) { this._blocks.Add(new TextBlock(newBlock.ToString())); newBlock.Clear(); } else if (line.ToUpper().Contains("{/ROUTES}")) { int idx = line.IndexOf("{/ROUTES}", 0, StringComparison.CurrentCultureIgnoreCase); int trashLength = idx + "{/ROUTES}".Length; string postText = line.Substring(trashLength, line.Length - trashLength); this._blocks.Add(new TextBlock(newBlock.ToString(), BlockType.Route, postText)); newBlock.Clear(); } else if (line.ToUpper().Contains("{HOPS}")) { this._blocks.Add(new TextBlock(newBlock.ToString(), BlockType.Route)); newBlock.Clear(); } else if (line.ToUpper().Contains("{/HOPS}")) { int idx = line.IndexOf("{/HOPS}", 0, StringComparison.CurrentCultureIgnoreCase); int trashLength = idx + "{/HOPS}".Length; string postText = line.Substring(trashLength, line.Length - trashLength); this._blocks.Last().InnerBlocks.Add(new TextBlock(newBlock.ToString(), BlockType.Hop, postText)); newBlock.Clear(); } else { newBlock.AppendLine(line); } } // If there is anything still in the read buffer add it as a new block. if (newBlock.Length > 0) { this._blocks.Add(new TextBlock(newBlock.ToString())); newBlock.Clear(); } } } } /// /// Read the associated template. /// /// Template file path. public void ReadTemplate(string TemplateFile) { this.ReadTemplate(new FileInfo(TemplateFile)); } /// /// Read the associated template. /// /// Template file. public void ReadTemplate(FileInfo TemplateFile) { this._templateFile = TemplateFile; this.ReadTemplate(); } /// /// Write data structure to a stream. /// /// Output stream. /// Array of trace route replies. public void ToStream(Stream OutputStream, TraceRoute[] Routes) { using (StreamWriter writer = new StreamWriter(OutputStream)) { foreach (TextBlock block in this._blocks) { switch (block.Type) { case BlockType.Route: foreach (TraceRoute route in Routes) { // Work with the copy dummy. string line = block.Text; int idx; // Search for destination tokens. while ((idx = line.IndexOf("{destination}", StringComparison.CurrentCultureIgnoreCase)) > -1) { line = line.Replace(line.Substring(idx, "{destination}".Length), route.Destination); } // Finally... writer.Write(line); writer.Flush(); // Loop through any inner blocks. foreach (TextBlock innerBlock in block.InnerBlocks) { this.WriteHops(writer, innerBlock, route.Hops.ToArray()); } if (!string.IsNullOrEmpty(block.PostText)) { // Add the separator if it is not the last route. if (route != Routes.Last()) { line += block.PostText; } // Finally... writer.Write(line); writer.Flush(); } } break; case BlockType.Hop: // Stub. break; default: // Nothing to see here. writer.Write(block.Text); writer.Flush(); break; } } } } /// /// Write data structure to a specific file. /// /// Target output file path. /// Array of trace route replies. /// A value indicating whether to append data on existing file contents. public void Write(string TargetFile, TraceRoute[] Routes, bool Append = false) { this.Write(new FileInfo(TargetFile), Routes, Append); } /// /// Write data structure to a specific file. /// /// Target output file. /// Array of trace route replies. /// A value indicating whether to append data on existing file contents. public void Write(FileInfo TargetFile, TraceRoute[] Routes, bool Append = false) { this.ToStream(TargetFile.Open(Append ? FileMode.Append : FileMode.Create), Routes); } private void WriteHops(StreamWriter writer, TextBlock block, Hop[] hops) { foreach (Hop hop in hops) { // Work with the copy dummy. string line = block.Text; int idx; // Search for address tokens. while ((idx = line.IndexOf("{address}", StringComparison.CurrentCultureIgnoreCase)) > -1) { line = line.Replace(line.Substring(idx, "{address}".Length), hop.Address); } // Search for hop tokens. while ((idx = line.IndexOf("{hop}", StringComparison.CurrentCultureIgnoreCase)) > -1) { line = line.Replace(line.Substring(idx, "{hop}".Length), hop.HopCount.ToString()); } // Search for host name tokens. while ((idx = line.IndexOf("{hostname}", StringComparison.CurrentCultureIgnoreCase)) > -1) { line = line.Replace(line.Substring(idx, "{hostname}".Length), hop.HostName); } // Search for round trip tokens. while ((idx = line.IndexOf("{roundtrip}", StringComparison.CurrentCultureIgnoreCase)) > -1) { line = line.Replace(line.Substring(idx, "{roundtrip}".Length), hop.RoundTrip.ToString()); } if (!string.IsNullOrEmpty(block.PostText)) { // Add the separator if it is not the last route. if (hop != hops.Last()) { line += block.PostText; } } // Finally... writer.Write(line); writer.Flush(); } } } }