- Moved commands outside TechBot.Library to TechBot.Commands.Common and TechBot.Comma...
[reactos.git] / irc / TechBot / TechBot.Commands.MSDN / ApiCommand.cs
1 using System;
2 using System.IO;
3 using System.Data;
4 using System.Text.RegularExpressions;
5
6 using HtmlHelp;
7 using HtmlHelp.ChmDecoding;
8
9 using TechBot.Library;
10
11 namespace TechBot.Commands.MSDN
12 {
13 [Command("api", Help = "!api <apiname>")]
14 public class ApiCommand : Command
15 {
16 private bool IsVerbose = false;
17
18 private HtmlHelpSystem chm;
19 private string name;
20
21 public ApiCommand()
22 {
23 Run();
24 }
25
26 [CommandParameter("api", "The API name")]
27 public string API
28 {
29 get { return Parameters; }
30 set { Parameters = value; }
31 }
32
33 private void WriteIfVerbose(string message)
34 {
35 if (IsVerbose)
36 Say(message);
37 }
38
39 private void Run()
40 {
41 string CHMFilename = Path.Combine(Settings.Default.ChmPath, Settings.Default.MainChm);
42 chm = new HtmlHelpSystem();
43 chm.OpenFile(CHMFilename, null);
44
45 Console.WriteLine(String.Format("Loaded main CHM: {0}",
46 Path.GetFileName(CHMFilename)));
47 foreach (string filename in Directory.GetFiles(Settings.Default.ChmPath))
48 {
49 if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
50 continue;
51 if (Path.GetFileName(filename).ToLower().Equals(Settings.Default.MainChm))
52 continue;
53
54 Console.WriteLine(String.Format("Loading CHM: {0}",
55 Path.GetFileName(filename)));
56 try
57 {
58 chm.MergeFile(filename);
59 }
60 catch (Exception ex)
61 {
62 Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
63 Path.GetFileName(filename),
64 ex));
65 }
66 }
67 Console.WriteLine(String.Format("Loaded {0} CHMs",
68 chm.FileList.Length));
69 }
70
71 public override void ExecuteCommand()
72 {
73 if (Name.Trim().Equals(String.Empty))
74 {
75 Say("Please give me a keyword.");
76 }
77 else
78 {
79 Search(Name);
80 }
81 }
82
83 private bool SearchIndex(
84 string keyword)
85 {
86 if (chm.HasIndex)
87 {
88 IndexItem item = chm.Index.SearchIndex(keyword,
89 IndexType.KeywordLinks);
90 if (item != null && item.Topics.Count > 0)
91 {
92 WriteIfVerbose(String.Format("Keyword {0} found in index",
93 item.KeyWord));
94 IndexTopic indexTopic = item.Topics[0] as IndexTopic;
95 return DisplayResult( keyword,
96 indexTopic);
97 }
98 else
99 {
100 WriteIfVerbose(String.Format("Keyword {0} not found in index",
101 keyword));
102 return false;
103 }
104 }
105 else
106 return false;
107 }
108
109 private void SearchFullText(string keyword)
110 {
111 string sort = "Rating ASC";
112 WriteIfVerbose(String.Format("Searching fulltext database for {0}",
113 keyword));
114
115 bool partialMatches = false;
116 bool titlesOnly = true;
117 int maxResults = 100;
118 DataTable results = chm.PerformSearch(keyword,
119 maxResults,
120 partialMatches,
121 titlesOnly);
122 WriteIfVerbose(String.Format("results.Rows.Count = {0}",
123 results != null ?
124 results.Rows.Count.ToString() : "(none)"));
125 if (results != null && results.Rows.Count > 0)
126 {
127 results.DefaultView.Sort = sort;
128 if (!DisplayResult(keyword,
129 results))
130 {
131 Say("No result");
132 }
133 }
134 else
135 {
136 Say("No result");
137 }
138 }
139
140 private void Search(string keyword)
141 {
142 if (!SearchIndex(keyword))
143 SearchFullText(keyword);
144 }
145
146 private bool DisplayResult(string keyword,
147 IndexTopic indexTopic)
148 {
149 keyword = keyword.Trim().ToLower();
150 string url = indexTopic.URL;
151 WriteIfVerbose(String.Format("URL from index search {0}",
152 url));
153 string prototype = ExtractPrototype(url);
154 if (prototype == null || prototype.Trim().Equals(String.Empty))
155 return false;
156 string formattedPrototype = FormatPrototype(prototype);
157 Say(formattedPrototype);
158 return true;
159 }
160
161 private bool DisplayResult(string keyword,
162 DataTable results)
163 {
164 keyword = keyword.Trim().ToLower();
165 for (int i = 0; i < results.DefaultView.Count; i++)
166 {
167 DataRowView row = results.DefaultView[i];
168 string title = row["Title"].ToString();
169 WriteIfVerbose(String.Format("Examining {0}", title));
170 if (title.Trim().ToLower().Equals(keyword))
171 {
172 string location = row["Location"].ToString();
173 string rating = row["Rating"].ToString();
174 string url = row["Url"].ToString();
175 string prototype = ExtractPrototype(url);
176 if (prototype == null || prototype.Trim().Equals(String.Empty))
177 continue;
178 string formattedPrototype = FormatPrototype(prototype);
179 Say(formattedPrototype);
180 return true;
181 }
182 }
183 return false;
184 }
185
186 private void DisplayNoResult(MessageContext context,
187 string keyword)
188 {
189 TechBot.ServiceOutput.WriteLine(context,
190 String.Format("I don't know about keyword {0}",
191 keyword));
192 }
193
194 private string ReplaceComments(string s)
195 {
196 return Regex.Replace(s, "//(.+)\r\n", "");
197 }
198
199 private string ReplaceLineEndings(string s)
200 {
201 return Regex.Replace(s, "(\r\n)+", " ");
202 }
203
204 private string ReplaceSpaces(string s)
205 {
206 return Regex.Replace(s, @" +", " ");
207 }
208
209 private string ReplaceSpacesBeforeLeftParenthesis(string s)
210 {
211 return Regex.Replace(s, @"\( ", @"(");
212 }
213
214 private string ReplaceSpacesBeforeRightParenthesis(string s)
215 {
216 return Regex.Replace(s, @" \)", @")");
217 }
218
219 private string ReplaceSemicolon(string s)
220 {
221 return Regex.Replace(s, @";", @"");
222 }
223
224 private string FormatPrototype(string prototype)
225 {
226 string s = ReplaceComments(prototype);
227 s = ReplaceLineEndings(s);
228 s = ReplaceSpaces(s);
229 s = ReplaceSpacesBeforeLeftParenthesis(s);
230 s = ReplaceSpacesBeforeRightParenthesis(s);
231 s = ReplaceSemicolon(s);
232 return s;
233 }
234
235 private string ExtractPrototype(string url)
236 {
237 string page = GetPage(url);
238 Match match = Regex.Match(page,
239 "<PRE class=\"?syntax\"?>(.+)</PRE>",
240 RegexOptions.Multiline |
241 RegexOptions.Singleline);
242 if (match.Groups.Count > 1)
243 {
244 string prototype = match.Groups[1].ToString();
245 return StripHtml(StripAfterSlashPre(prototype));
246 }
247
248 return "";
249 }
250
251 private string StripAfterSlashPre(string html)
252 {
253 int index = html.IndexOf("</PRE>");
254 if (index != -1)
255 {
256 return html.Substring(0, index);
257 }
258 else
259 return html;
260 }
261
262 private string StripHtml(string html)
263 {
264 return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
265 }
266
267 private string GetPage(string url)
268 {
269 string CHMFileName = "";
270 string topicName = "";
271 string anchor = "";
272 CHMStream.CHMStream baseStream;
273 if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
274 {
275 baseStream = chm.BaseStream;
276 CHMFileName = baseStream.CHMFileName;
277 topicName = url;
278 anchor = "";
279 }
280 else
281 {
282 baseStream = GetBaseStreamFromCHMFileName(CHMFileName);
283 }
284
285 if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
286 {
287 return "";
288 }
289
290 return baseStream.ExtractTextFile(topicName);
291 }
292
293 private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName)
294 {
295 foreach (CHMFile file in chm.FileList)
296 {
297 WriteIfVerbose(String.Format("Compare: {0} <> {1}",
298 file.ChmFilePath,
299 CHMFileName));
300 if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
301 {
302 return file.BaseStream;
303 }
304 }
305 WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}",
306 CHMFileName));
307 return null;
308 }
309 }
310 }