+using System;
+using System.IO;
+using System.Data;
+using System.Text.RegularExpressions;
+
+using HtmlHelp;
+using HtmlHelp.ChmDecoding;
+
+using TechBot.Library;
+
+namespace TechBot.Commands.MSDN
+{
+ [Command("api", Help = "!api <apiname>")]
+ public class ApiCommand : Command
+ {
+ private bool IsVerbose = false;
+
+ private HtmlHelpSystem chm;
+ private string name;
+
+ public ApiCommand()
+ {
+ Run();
+ }
+
+ [CommandParameter("api", "The API name")]
+ public string API
+ {
+ get { return Parameters; }
+ set { Parameters = value; }
+ }
+
+ private void WriteIfVerbose(string message)
+ {
+ if (IsVerbose)
+ Say(message);
+ }
+
+ private void Run()
+ {
+ string CHMFilename = Path.Combine(Settings.Default.ChmPath, Settings.Default.MainChm);
+ chm = new HtmlHelpSystem();
+ chm.OpenFile(CHMFilename, null);
+
+ Console.WriteLine(String.Format("Loaded main CHM: {0}",
+ Path.GetFileName(CHMFilename)));
+ foreach (string filename in Directory.GetFiles(Settings.Default.ChmPath))
+ {
+ if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
+ continue;
+ if (Path.GetFileName(filename).ToLower().Equals(Settings.Default.MainChm))
+ continue;
+
+ Console.WriteLine(String.Format("Loading CHM: {0}",
+ Path.GetFileName(filename)));
+ try
+ {
+ chm.MergeFile(filename);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
+ Path.GetFileName(filename),
+ ex));
+ }
+ }
+ Console.WriteLine(String.Format("Loaded {0} CHMs",
+ chm.FileList.Length));
+ }
+
+ public override void ExecuteCommand()
+ {
+ if (Name.Trim().Equals(String.Empty))
+ {
+ Say("Please give me a keyword.");
+ }
+ else
+ {
+ Search(Name);
+ }
+ }
+
+ private bool SearchIndex(
+ string keyword)
+ {
+ if (chm.HasIndex)
+ {
+ IndexItem item = chm.Index.SearchIndex(keyword,
+ IndexType.KeywordLinks);
+ if (item != null && item.Topics.Count > 0)
+ {
+ WriteIfVerbose(String.Format("Keyword {0} found in index",
+ item.KeyWord));
+ IndexTopic indexTopic = item.Topics[0] as IndexTopic;
+ return DisplayResult( keyword,
+ indexTopic);
+ }
+ else
+ {
+ WriteIfVerbose(String.Format("Keyword {0} not found in index",
+ keyword));
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ private void SearchFullText(string keyword)
+ {
+ string sort = "Rating ASC";
+ WriteIfVerbose(String.Format("Searching fulltext database for {0}",
+ keyword));
+
+ bool partialMatches = false;
+ bool titlesOnly = true;
+ int maxResults = 100;
+ DataTable results = chm.PerformSearch(keyword,
+ maxResults,
+ partialMatches,
+ titlesOnly);
+ WriteIfVerbose(String.Format("results.Rows.Count = {0}",
+ results != null ?
+ results.Rows.Count.ToString() : "(none)"));
+ if (results != null && results.Rows.Count > 0)
+ {
+ results.DefaultView.Sort = sort;
+ if (!DisplayResult(keyword,
+ results))
+ {
+ Say("No result");
+ }
+ }
+ else
+ {
+ Say("No result");
+ }
+ }
+
+ private void Search(string keyword)
+ {
+ if (!SearchIndex(keyword))
+ SearchFullText(keyword);
+ }
+
+ private bool DisplayResult(string keyword,
+ IndexTopic indexTopic)
+ {
+ keyword = keyword.Trim().ToLower();
+ string url = indexTopic.URL;
+ WriteIfVerbose(String.Format("URL from index search {0}",
+ url));
+ string prototype = ExtractPrototype(url);
+ if (prototype == null || prototype.Trim().Equals(String.Empty))
+ return false;
+ string formattedPrototype = FormatPrototype(prototype);
+ Say(formattedPrototype);
+ return true;
+ }
+
+ private bool DisplayResult(string keyword,
+ DataTable results)
+ {
+ keyword = keyword.Trim().ToLower();
+ for (int i = 0; i < results.DefaultView.Count; i++)
+ {
+ DataRowView row = results.DefaultView[i];
+ string title = row["Title"].ToString();
+ WriteIfVerbose(String.Format("Examining {0}", title));
+ if (title.Trim().ToLower().Equals(keyword))
+ {
+ string location = row["Location"].ToString();
+ string rating = row["Rating"].ToString();
+ string url = row["Url"].ToString();
+ string prototype = ExtractPrototype(url);
+ if (prototype == null || prototype.Trim().Equals(String.Empty))
+ continue;
+ string formattedPrototype = FormatPrototype(prototype);
+ Say(formattedPrototype);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void DisplayNoResult(MessageContext context,
+ string keyword)
+ {
+ TechBot.ServiceOutput.WriteLine(context,
+ String.Format("I don't know about keyword {0}",
+ keyword));
+ }
+
+ private string ReplaceComments(string s)
+ {
+ return Regex.Replace(s, "//(.+)\r\n", "");
+ }
+
+ private string ReplaceLineEndings(string s)
+ {
+ return Regex.Replace(s, "(\r\n)+", " ");
+ }
+
+ private string ReplaceSpaces(string s)
+ {
+ return Regex.Replace(s, @" +", " ");
+ }
+
+ private string ReplaceSpacesBeforeLeftParenthesis(string s)
+ {
+ return Regex.Replace(s, @"\( ", @"(");
+ }
+
+ private string ReplaceSpacesBeforeRightParenthesis(string s)
+ {
+ return Regex.Replace(s, @" \)", @")");
+ }
+
+ private string ReplaceSemicolon(string s)
+ {
+ return Regex.Replace(s, @";", @"");
+ }
+
+ private string FormatPrototype(string prototype)
+ {
+ string s = ReplaceComments(prototype);
+ s = ReplaceLineEndings(s);
+ s = ReplaceSpaces(s);
+ s = ReplaceSpacesBeforeLeftParenthesis(s);
+ s = ReplaceSpacesBeforeRightParenthesis(s);
+ s = ReplaceSemicolon(s);
+ return s;
+ }
+
+ private string ExtractPrototype(string url)
+ {
+ string page = GetPage(url);
+ Match match = Regex.Match(page,
+ "<PRE class=\"?syntax\"?>(.+)</PRE>",
+ RegexOptions.Multiline |
+ RegexOptions.Singleline);
+ if (match.Groups.Count > 1)
+ {
+ string prototype = match.Groups[1].ToString();
+ return StripHtml(StripAfterSlashPre(prototype));
+ }
+
+ return "";
+ }
+
+ private string StripAfterSlashPre(string html)
+ {
+ int index = html.IndexOf("</PRE>");
+ if (index != -1)
+ {
+ return html.Substring(0, index);
+ }
+ else
+ return html;
+ }
+
+ private string StripHtml(string html)
+ {
+ return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
+ }
+
+ private string GetPage(string url)
+ {
+ string CHMFileName = "";
+ string topicName = "";
+ string anchor = "";
+ CHMStream.CHMStream baseStream;
+ if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
+ {
+ baseStream = chm.BaseStream;
+ CHMFileName = baseStream.CHMFileName;
+ topicName = url;
+ anchor = "";
+ }
+ else
+ {
+ baseStream = GetBaseStreamFromCHMFileName(CHMFileName);
+ }
+
+ if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
+ {
+ return "";
+ }
+
+ return baseStream.ExtractTextFile(topicName);
+ }
+
+ private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName)
+ {
+ foreach (CHMFile file in chm.FileList)
+ {
+ WriteIfVerbose(String.Format("Compare: {0} <> {1}",
+ file.ChmFilePath,
+ CHMFileName));
+ if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
+ {
+ return file.BaseStream;
+ }
+ }
+ WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}",
+ CHMFileName));
+ return null;
+ }
+ }
+}