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