0
|
1 using System;
|
|
2 using System.Collections.Generic;
|
|
3 using System.Globalization;
|
|
4 using System.IO;
|
|
5 using System.Linq;
|
|
6 using System.Text.RegularExpressions;
|
|
7 using System.Xml;
|
|
8
|
|
9 using Rsdn.Framework.Formatting;
|
|
10
|
|
11 namespace DocGen
|
|
12 {
|
|
13 delegate FileAction FileActionHandler(string ext);
|
|
14
|
|
15 partial class Generator
|
|
16 {
|
|
17 string _sourcePath;
|
|
18 string _destFolder;
|
|
19 FileActionHandler _fileAction;
|
|
20
|
|
21 public void Generate(
|
|
22 FileItem createdFiles,
|
|
23 string templateFileName,
|
|
24 string[] path,
|
|
25 string destFolder,
|
|
26 string sourcePath,
|
|
27 bool cleanUp,
|
|
28 bool createIndex,
|
|
29 FileActionHandler fileAction)
|
|
30 {
|
|
31 _sourcePath = Path.GetFullPath(sourcePath);
|
|
32 _destFolder = Path.GetFullPath(destFolder);
|
|
33 _fileAction = fileAction;
|
|
34
|
|
35 if (cleanUp)
|
|
36 CleanUp();
|
|
37
|
|
38 CreateDestFolder();
|
|
39
|
|
40 var template = File.ReadAllText(templateFileName);
|
|
41
|
|
42 GenerateContent(createdFiles, template, path, createIndex);
|
|
43 }
|
|
44
|
|
45 private void CreateDestFolder()
|
|
46 {
|
|
47 if (Directory.Exists(_destFolder) == false)
|
|
48 Directory.CreateDirectory(_destFolder);
|
|
49 }
|
|
50
|
|
51 private void CleanUp()
|
|
52 {
|
|
53 if (Directory.Exists(_destFolder) == false)
|
|
54 return;
|
|
55
|
|
56 Action<string> clean = null; clean = delegate(string path)
|
|
57 {
|
|
58 foreach (var file in Directory.GetFiles(path))
|
|
59 if (Path.GetExtension(file).ToLower() != ".zip")
|
|
60 try { File.Delete(file); } catch {}
|
|
61
|
|
62 foreach (var dir in Directory.GetDirectories(path))
|
|
63 {
|
|
64 clean(dir);
|
|
65 try { Directory.Delete(dir); } catch {}
|
|
66 }
|
|
67 };
|
|
68
|
|
69 clean(_destFolder);
|
|
70 }
|
|
71
|
|
72 static readonly Regex ct_item1 = new Regex(@"<ct_item\s*link\=(?<link>.*?)\s*label=['""](?<label>.*?)['""]\s*/>");
|
|
73 static readonly Regex ct_item2 = new Regex(@"<ct_item\s*link\=(?<link>.*?)\s*label=['""](?<label>.*?)['""]\s*>(?<text>.*?)</ct_item>", RegexOptions.Singleline);
|
|
74 static readonly Regex ct_item3 = new Regex(@"<mt_item\s*link\=(?<link>.*?)\s*label=['""](?<label>.*?)['""]\s*>(?<text>.*?)</mt_item>", RegexOptions.Singleline);
|
|
75 static readonly Regex ct_item4 = new Regex(@"<ct_item2\s*link1\=(?<link1>.*?)\s*label1=['""](?<label1>.*?)['""]\s*link2\=(?<link2>.*?)\s*label2=['""](?<label2>.*?)['""]\s*/>", RegexOptions.Singleline);
|
|
76 static readonly Regex ct_item5 = new Regex(@"<ct_item3\s*link1\=(?<link1>.*?)\s*label1=['""](?<label1>.*?)['""]\s*link2\=(?<link2>.*?)\s*label2=['""](?<label2>.*?)['""]\s*link3\=(?<link3>.*?)\s*label3=['""](?<label3>.*?)['""]\s*/>", RegexOptions.Singleline);
|
|
77
|
|
78 private bool GenerateContent(
|
|
79 FileItem createdFiles, string template, string[] path, bool createIndex)
|
|
80 {
|
|
81 var folder = string.Join("/", path);
|
|
82 var destFolder = Path.Combine(_destFolder, folder);
|
|
83 var backPath = "";
|
|
84
|
|
85 for (var i = 0; i < path.Length; i++)
|
|
86 backPath += "../";
|
|
87
|
|
88 var sourcePath = Path.Combine(_sourcePath, folder);
|
|
89 var sourceFiles = Directory.GetFiles(sourcePath);
|
|
90
|
|
91 var files = new List<string>();
|
|
92 var folders = new List<string>();
|
|
93
|
|
94 foreach (var fileName in sourceFiles)
|
|
95 {
|
|
96 var backLinks = GeneratePath(path, backPath, fileName);
|
|
97
|
|
98 var destName = Path.Combine(destFolder, Path.GetFileName(fileName));
|
|
99 var ext = Path.GetExtension(destName).ToLower();
|
|
100
|
|
101 Console.WriteLine(destName);
|
|
102
|
|
103 switch (_fileAction(fileName))
|
|
104 {
|
|
105 case FileAction.Skip:
|
|
106 break;
|
|
107
|
|
108 case FileAction.Copy:
|
|
109 File.Copy(fileName, destName, true);
|
|
110 break;
|
|
111
|
|
112 case FileAction.Process:
|
|
113 if (Directory.Exists(destFolder) == false)
|
|
114 Directory.CreateDirectory(destFolder);
|
|
115
|
|
116 switch (ext)
|
|
117 {
|
|
118 case ".htm":
|
|
119 case ".html":
|
|
120 using (var sw = File.CreateText(destName))
|
|
121 using (var sr = File.OpenText(fileName))
|
|
122 {
|
|
123 var item = new FileItem { IsFile = true, Name = destName };
|
|
124 createdFiles.Add(item);
|
|
125
|
|
126 var source = sr.ReadToEnd();
|
|
127
|
|
128 source = source
|
|
129 .Replace("<ct_table>", "<table border='0' cellpadding='0' cellspacing='0'>")
|
|
130 .Replace("<ct_hr>", "<ct_mg><tr><td colspan='5' class='hr'><img width='1' height='1' alt=''/></td></tr><ct_mg>")
|
|
131 .Replace("<ct_text>", "<tr><td colspan='5'>")
|
|
132 .Replace("</ct_text>", "</td></tr><ct_mg>")
|
|
133 .Replace("<ct_mg>", "<tr><td colspan='5' class='sp'><img width='1' height='1' alt=''/></td></tr>")
|
|
134 .Replace("</ct_table>", "</table>")
|
|
135 ;
|
|
136
|
|
137 source = ct_item1.Replace(source, @"<tr><td nowrap colspan='5'>• <a href=${link}>${label}</a></td></tr>");
|
|
138 source = ct_item2.Replace(source, @"<tr><td nowrap>• <a href=${link}>${label}</a></td><td> </td><td class='j' colspan='3'>${text}</td></tr>");
|
|
139 source = ct_item3.Replace(source, @"<tr><td nowrap class='p'>• <a href=${link}>${label}</a></td><td></td><td class='pj' colspan='3'>${text}</td></tr>");
|
|
140 source = ct_item4.Replace(source, @"<tr><td nowrap>• <a href=${link1}>${label1}</a></td><td> </td><td nowrap colspan='3'>• <a href=${link2}>${label2}</a></td></tr>");
|
|
141 source = ct_item5.Replace(source, @"<tr><td nowrap>• <a href=${link1}>${label1}</a></td><td> </td><td nowrap>• <a href=${link2}>${label2}</a></td><td> </td><td nowrap>• <a href=${link3}>${label3}</a></td></tr>");
|
|
142
|
|
143 if (_modifySourceLinks)
|
|
144 {
|
|
145 source = source
|
|
146 .Replace("href=\"..\\..\\..\\Source\\", "target=_blank href=\"/Source/")
|
|
147 .Replace("href='..\\..\\..\\Source\\", "target=_blank href='/Source/")
|
|
148 .Replace("<a href=\"http://", "<a target=_blank href=\"http://")
|
|
149 .Replace("<a href='http://", "<a target=_blank href='http://")
|
|
150 ;
|
|
151 }
|
|
152
|
|
153 var title = item.Title;
|
|
154
|
|
155 if (title == "index")
|
|
156 {
|
|
157 title = Path.GetFileName(Path.GetDirectoryName(fileName));
|
|
158
|
|
159 if (title != "content")
|
|
160 item.Title = title;
|
|
161 }
|
|
162
|
|
163 source = GenerateSource(source, item);
|
|
164 title = item.Title;
|
|
165
|
|
166 if (title.Length > 0 && _addDashToTitle)
|
|
167 title += " - ";
|
|
168
|
|
169 sw.WriteLine(string.Format(
|
|
170 template,
|
|
171 source,
|
|
172 backPath,
|
|
173 backLinks,
|
|
174 title));
|
|
175
|
|
176 if (item.NoIndex == false)
|
|
177 {
|
|
178 source = source
|
|
179 .Replace("<span class='a'>", "")
|
|
180 .Replace("</span>", "")
|
|
181 .Replace("<", "<")
|
|
182 .Replace(">", ">")
|
|
183 ;
|
|
184
|
|
185 foreach (var index in IndexItem.Index)
|
|
186 if (!item.NoIndexes.Contains(index.Name))
|
|
187 foreach (var s in index.Text)
|
|
188 if (source.IndexOf(s) >= 0)
|
|
189 {
|
|
190 index.Files.Add(item);
|
|
191 break;
|
|
192 }
|
|
193
|
|
194 foreach (var s in item.Indexes)
|
|
195 {
|
|
196 var index = IndexItem.Index.Find(i => i.Name == s);
|
|
197
|
|
198 if (index == null)
|
|
199 IndexItem.Index.Add(new IndexItem(s));
|
|
200
|
|
201 if (index.Files.Contains(item) == false)
|
|
202 index.Files.Add(item);
|
|
203 }
|
|
204 }
|
|
205 }
|
|
206
|
|
207 break;
|
|
208
|
|
209 case ".cs":
|
|
210 using (var sw = File.CreateText(destName + ".htm"))
|
|
211 {
|
|
212 createdFiles.Add(new FileItem { IsFile = true, Name = destName + ".htm" });
|
|
213
|
|
214 var source = GenerateSource("<% " + fileName + " %>", null);
|
|
215
|
|
216 sw.WriteLine(string.Format(
|
|
217 template,
|
|
218 source,
|
|
219 backPath,
|
|
220 backLinks,
|
|
221 Path.GetFileNameWithoutExtension(fileName) + " - "));
|
|
222 }
|
|
223 break;
|
|
224 }
|
|
225
|
|
226 files.Add(fileName);
|
|
227
|
|
228 break;
|
|
229 }
|
|
230 }
|
|
231
|
|
232 var dirs = Directory.GetDirectories(sourcePath);
|
|
233 var newPath = new string[path.Length + 1];
|
|
234
|
|
235 path.CopyTo(newPath, 0);
|
|
236
|
|
237 foreach (var dir in dirs)
|
|
238 {
|
|
239 var dirList = dir.Split('/', '\\');
|
|
240 var dirName = dirList[dirList.Length - 1];
|
|
241
|
|
242 // Skip Subversion folders.
|
|
243 //
|
|
244 if (dirName == "_svn" || dirName == ".svn")
|
|
245 continue;
|
|
246
|
|
247 newPath[path.Length] = dirName;
|
|
248
|
|
249 var item = new FileItem { IsFile = false, Name = dirName};
|
|
250
|
|
251 createdFiles.Add(item);
|
|
252
|
|
253 if (GenerateContent(item, template, newPath, createIndex))
|
|
254 folders.Add(dir);
|
|
255 }
|
|
256
|
|
257 if (files.Count > 0 || folders.Count > 0)
|
|
258 {
|
|
259 var indexName = destFolder + "/index.htm";
|
|
260
|
|
261 if (createIndex && File.Exists(indexName) == false)
|
|
262 {
|
|
263 var str = "";
|
|
264
|
|
265 folders.Sort();
|
|
266
|
|
267 foreach (var s in folders)
|
|
268 str += string.Format("• <a href='{0}/index.htm'>{0}</a><br>\n",
|
|
269 Path.GetFileName(s));
|
|
270
|
|
271 if (str.Length > 0)
|
|
272 str += "<br>";
|
|
273
|
|
274 files.Sort();
|
|
275
|
|
276 foreach (var s in files)
|
|
277 str += string.Format(
|
|
278 s.EndsWith(".htm", true, CultureInfo.CurrentCulture) ||
|
|
279 s.EndsWith(".html", true, CultureInfo.CurrentCulture)?
|
|
280 "• <a href='{0}'>{0}</a><br>":
|
|
281 "• <a href='{0}.htm'>{0}</a><br>\n",
|
|
282 Path.GetFileName(s));
|
|
283
|
|
284 _fileAction(indexName);
|
|
285
|
|
286 using (var sw = File.CreateText(indexName))
|
|
287 {
|
|
288 createdFiles.Add(new FileItem { IsFile = true, Name = indexName });
|
|
289
|
|
290 sw.WriteLine(string.Format(
|
|
291 template,
|
|
292 str,
|
|
293 backPath,
|
|
294 GeneratePath(path, backPath, "@@@").Replace(".@@@", ""),
|
|
295 Path.GetFileNameWithoutExtension(destFolder) + " - "));
|
|
296 }
|
|
297 }
|
|
298
|
|
299 return true;
|
|
300 }
|
|
301
|
|
302 return false;
|
|
303 }
|
|
304
|
|
305 private string GenerateSource(string text, FileItem item)
|
|
306 {
|
|
307 for (int
|
|
308 idx = text.IndexOf("<%"),
|
|
309 end = text.IndexOf("%>", idx + 2);
|
|
310 idx >= 0 &&
|
|
311 end >= 0;
|
|
312 idx = text.IndexOf("<%", idx + 2),
|
|
313 end = text.IndexOf("%>", idx + 2))
|
|
314 {
|
|
315 var startSource = text.Substring(0, idx);
|
|
316 var source = text.Substring(idx + 2, end - idx - 2).Trim();
|
|
317 var command = "source";
|
|
318
|
|
319 var cmdIdx = source.IndexOf('#');
|
|
320
|
|
321 if (cmdIdx >= 0)
|
|
322 {
|
|
323 command = source.Substring(0, cmdIdx).Trim().ToLower();
|
|
324 source = source.Substring(cmdIdx+1).Trim();
|
|
325 }
|
|
326
|
|
327 switch (command)
|
|
328 {
|
|
329 case "source" : source = GetSourceCodeFromPath(Path.Combine(_sourcePath, source), text); break;
|
|
330 case "rss" : source = GetNews (Path.Combine(_sourcePath, source)); break;
|
|
331 case "txt" :
|
|
332 case "cs" :
|
|
333 case "sql" : source = GetSourceCode(source, "." + command, text); break;
|
|
334 case "title" : item.Title = source; source = ""; break;
|
|
335 case "order" : item.SortOrder = int.Parse(source); source = ""; break;
|
|
336 case "group" : item.Group = source; source = ""; break;
|
|
337 case "index" : item.Indexes.Add(source); source = ""; break;
|
|
338 case "table" : source = GetTable(Path.Combine(_sourcePath, source)); break;
|
|
339 case "noindex" :
|
|
340 if (source.Length == 0)
|
|
341 item.NoIndex = true;
|
|
342 else
|
|
343 item.NoIndexes.Add(source);
|
|
344
|
|
345 source = "";
|
|
346 break;
|
|
347
|
|
348 default : throw new InvalidOperationException();
|
|
349 }
|
|
350
|
|
351 text = startSource + source + text.Substring(end + 2);
|
|
352 }
|
|
353
|
|
354 return text
|
|
355 .Replace(@"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=DBHost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=TestUser;Password=TestPassword;", "...");
|
|
356 }
|
|
357
|
|
358 private static string GetNews(string sourcePath)
|
|
359 {
|
|
360 var doc = new XmlDocument();
|
|
361
|
|
362 doc.Load(sourcePath);
|
|
363
|
|
364 var html = "<table border='0' cellpadding='0' cellspacing='0'>";
|
|
365 var @class = "";
|
|
366 var i = 0;
|
|
367
|
|
368 foreach (XmlNode item in doc.SelectNodes("rss/channel/item"))
|
|
369 {
|
|
370 html += string.Format(@"
|
|
371 <tr><td{0} colspan='2'><nobr><b>{1:MM/dd/yy}</nobr></b> <a href='{2}'>{3}</a></td></tr>
|
|
372 <tr><td> </td><td class='j'>{4}</td></tr>
|
|
373 ",
|
|
374 @class,
|
|
375 DateTime.Parse(item.SelectSingleNode("pubDate").InnerText),
|
|
376 item.SelectSingleNode("link"). InnerText,
|
|
377 item.SelectSingleNode("title"). InnerText,
|
|
378 item.SelectSingleNode("description").InnerText);
|
|
379
|
|
380 @class = " class='p'";
|
|
381
|
|
382 if (++i == 20)
|
|
383 break;
|
|
384 }
|
|
385
|
|
386 html += "</table>";
|
|
387
|
|
388 return html;
|
|
389 }
|
|
390
|
|
391 private static string GetSourceCode(string code, string ext, string source)
|
|
392 {
|
|
393 switch (ext)
|
|
394 {
|
|
395 case ".cpp": code = "[c]" + code + "[/c]"; break;
|
|
396 case ".cs": code = "[c#]" + code + "[/c#]"; break;
|
|
397 case ".vb": code = "[vb]" + code + "[/vb]"; break;
|
|
398 case ".xml":
|
|
399 case ".config": code = "[xml]" + code + "[/xml]"; break;
|
|
400 case ".sql": code = "[sql]" + code + "[/sql]"; break;
|
|
401 default : code = "[code]" + code + "[/code]"; break;
|
|
402 }
|
|
403
|
|
404 code = code
|
|
405 .Replace("/*[", "[")
|
|
406 .Replace("]*/", "]")
|
|
407 ;
|
|
408
|
|
409 code = new TextFormatter().Format(code, false);
|
|
410
|
|
411 if (source.IndexOf("<a name='Person'></a>") >= 0)
|
|
412 code = code
|
|
413 .Replace("<Person>", "<<a class=m href=#Person>Person</a>>")
|
|
414 .Replace(", Person>", ", <a class=m href=#Person>Person</a>>")
|
|
415 .Replace(" Person ", " <a class='m' href=#Person>Person</a> ")
|
|
416 .Replace(" Person()", " <a class='m' href=#Person>Person</a>()")
|
|
417 .Replace("(Person ", "(<a class='m' href=#Person>Person</a> ")
|
|
418 ;
|
|
419
|
|
420 return code
|
|
421 .Replace("\n", "\r\n")
|
|
422 .Replace("\r\r\n", "\r\n")
|
|
423 .Replace("<table width='96%'>", "<table width='100%' class='code'>")
|
|
424 .Replace("<pre>", "<pre class='code'>")
|
|
425 .Replace("[a]", "<span class='a'>")
|
|
426 .Replace("[/a]", "</span>")
|
|
427 .Replace("[link]", "<a ")
|
|
428 .Replace("[/link]", "</a>")
|
|
429 .Replace("[file]", "href='/Source/")
|
|
430 .Replace("[/file]", ".htm'>")
|
|
431 .Replace("<!--", "<span class='com'><!--")
|
|
432 .Replace("-->", "--></span>")
|
|
433 ;
|
|
434 }
|
|
435
|
|
436 private static string GetSourceCodeFromPath(string sourcePath, string source)
|
|
437 {
|
|
438 var code = "";
|
|
439
|
|
440 using (var sr = File.OpenText(sourcePath))
|
|
441 for (var s = sr.ReadLine(); s != null; s = sr.ReadLine())
|
|
442 if (!s.StartsWith("//@") && !s.StartsWith("''@"))
|
|
443 code += s + "\n";
|
|
444
|
|
445 return GetSourceCode(code, Path.GetExtension(sourcePath).ToLower(), source);
|
|
446 }
|
|
447
|
|
448 private static string GeneratePath(string[] path, string backPath, string fileName)
|
|
449 {
|
|
450 if (path.Length == 0)
|
|
451 return "";
|
|
452
|
|
453 var backLinks = "";
|
|
454 var parent = "";
|
|
455 var name = Path.GetFileNameWithoutExtension(fileName);
|
|
456
|
|
457 switch (path[0])
|
|
458 {
|
|
459 case "Doc":
|
|
460 backLinks += string.Format(
|
|
461 "<br><nobr> <small><a class='m' href='{0}Doc/index.htm'>Doc</a>",
|
|
462 backPath);
|
|
463
|
|
464 for (var i = 1; i < path.Length; i++)
|
|
465 {
|
|
466 parent = "";
|
|
467
|
|
468 for (var j = i + 1; j < path.Length; j++)
|
|
469 parent += "../";
|
|
470
|
|
471 backLinks += string.Format(".<a class='m' href='{0}index.htm'>{1}</a>", parent, path[i]);
|
|
472 }
|
|
473
|
|
474 if (name.ToLower() != "index")
|
|
475 {
|
|
476 backLinks += string.Format(".<a class='m' href='{0}{1}'>{2}</a>",
|
|
477 parent, Path.GetFileName(fileName), name);
|
|
478 }
|
|
479
|
|
480 backLinks += "<small></nobr></br>";
|
|
481
|
|
482 break;
|
|
483
|
|
484 case "Source":
|
|
485 backLinks += string.Format(
|
|
486 "<br><nobr> <small><a class='m' href='{0}Source/index.htm'>Source</a>",
|
|
487 backPath);
|
|
488
|
|
489 for (var i = 1; i < path.Length; i++)
|
|
490 {
|
|
491 parent = "";
|
|
492
|
|
493 for (var j = i + 1; j < path.Length; j++)
|
|
494 parent += "../";
|
|
495
|
|
496 backLinks += string.Format(".<a class='m' href='{0}index.htm'>{1}</a>", parent, path[i]);
|
|
497 }
|
|
498
|
|
499 if (name.ToLower() != "@@@")
|
|
500 {
|
|
501 backLinks += string.Format(".<a class='m' href='{0}{1}.htm'>{1}</a>",
|
|
502 parent, Path.GetFileName(fileName));
|
|
503 }
|
|
504
|
|
505 backLinks += "<small></nobr></br>";
|
|
506
|
|
507 break;
|
|
508 }
|
|
509
|
|
510 return backLinks;
|
|
511 }
|
|
512
|
|
513 class TableItem
|
|
514 {
|
|
515 public string Provider;
|
|
516 public string Feature;
|
|
517 public string Linq;
|
|
518 public string Implementation;
|
|
519 }
|
|
520
|
|
521 static string _providerName;
|
|
522
|
|
523 static TableItem GetTableItem(string line)
|
|
524 {
|
|
525 if (line.StartsWith("*"))
|
|
526 {
|
|
527 _providerName = line.Substring(1).Trim();
|
|
528 return null;
|
|
529 }
|
|
530
|
|
531 var ss = line.Replace("||", "$$$$$").Split('|');
|
|
532
|
|
533 if (ss.Length != 4)
|
|
534 throw new InvalidOperationException(line);
|
|
535
|
|
536 var impl = ss[3].Trim().Replace("$$$$$", "||");
|
|
537
|
|
538 if (impl.StartsWith("#"))
|
|
539 {
|
|
540 impl = impl.Substring(impl.IndexOf(' ')).Replace("\\n", "\n").Replace("\\t", "\t").Trim();
|
|
541 impl = GetSourceCode(impl, ".sql", "");
|
|
542 }
|
|
543
|
|
544 return new TableItem
|
|
545 {
|
|
546 Provider = _providerName,
|
|
547 Feature = ss[1].Trim().Replace("$$$$$", "||"),
|
|
548 Linq = ss[2].Trim().Replace("$$$$$", "||"),
|
|
549 Implementation = impl
|
|
550 };
|
|
551 }
|
|
552
|
|
553 static string GetTable(string sourcePath)
|
|
554 {
|
|
555 Console.WriteLine("table {0}", sourcePath);
|
|
556
|
|
557 var lines = File.ReadAllLines(sourcePath);
|
|
558 var items = (from l in lines where l.Trim().Length > 0 select GetTableItem(l)).Where(i => i != null).ToList();
|
|
559 var providers = (from i in items where i.Provider.Length > 0 group i by i.Provider into g select g.Key).ToList();
|
|
560 var forall = (from i in items where i.Provider.Length == 0 select i).ToList();
|
|
561
|
|
562 foreach (var p in providers)
|
|
563 {
|
|
564 items.AddRange(from a in forall select new TableItem
|
|
565 {
|
|
566 Provider = p,
|
|
567 Feature = a.Feature,
|
|
568 Linq = a.Linq,
|
|
569 Implementation = a.Implementation
|
|
570 });
|
|
571 }
|
|
572
|
|
573 foreach (var i in forall)
|
|
574 items.Remove(i);
|
|
575
|
|
576 var features = (
|
|
577 from i in items
|
|
578 group i by new { i.Feature, i.Linq } into g
|
|
579 orderby g.Key.Feature, g.Key.Linq
|
|
580 select new
|
|
581 {
|
|
582 g.Key.Feature,
|
|
583 g.Key.Linq,
|
|
584 Providers = new string[providers.Count()]
|
|
585 }
|
|
586 ).ToList();
|
|
587
|
|
588 foreach (var f in features)
|
|
589 {
|
|
590 for (var i = 0; i < providers.Count; i++)
|
|
591 {
|
|
592 f.Providers[i] = (
|
|
593 from it in items
|
|
594 where it.Provider == providers[i] && it.Feature == f.Feature && it.Linq == f.Linq
|
|
595 select it.Implementation
|
|
596 ).FirstOrDefault();
|
|
597 }
|
|
598 }
|
|
599
|
|
600 var s = "<table class='data bordered nowrappable'>\n<tr><th> </th><th>Linq</th>";
|
|
601
|
|
602 foreach (var p in providers)
|
|
603 s += "<th>" + p + "</th>";
|
|
604
|
|
605 s += "</tr>";
|
|
606
|
|
607 var byFeature = from f in features group f by f.Feature;
|
|
608
|
|
609 foreach (var f in byFeature)
|
|
610 {
|
|
611 bool first = true;
|
|
612
|
|
613 foreach (var l in f)
|
|
614 {
|
|
615 s += "<tr>";
|
|
616
|
|
617 if (first)
|
|
618 {
|
|
619 s += f.Count() == 1? "<td>": "<td rowspan=" + f.Count() + ">";
|
|
620 s += f.Key + "</td>";
|
|
621 first = false;
|
|
622 }
|
|
623
|
|
624 s += "<td>" + l.Linq + "</td>";
|
|
625
|
|
626 var n = 0;
|
|
627 string p = null;
|
|
628
|
|
629 for (var i = 0; i < l.Providers.Count(); i++)
|
|
630 {
|
|
631 if (l.Providers[i] == p)
|
|
632 n++;
|
|
633 else
|
|
634 {
|
|
635 if (n > 0)
|
|
636 {
|
|
637 s += n == 1? "<td": "<td colspan=" + n;
|
|
638 s += p == null ? " class=nosup>" : ">";
|
|
639 s += p ?? "X";
|
|
640 s += "</td>";
|
|
641 }
|
|
642
|
|
643 p = l.Providers[i];
|
|
644 n = 1;
|
|
645 }
|
|
646 }
|
|
647
|
|
648 if (n > 0)
|
|
649 {
|
|
650 s += n == 1? "<td": "<td colspan=" + n;
|
|
651 s += p == null ? " class=nosup>" : ">";
|
|
652 s += p ?? "X";
|
|
653 s += "</td>";
|
|
654 }
|
|
655
|
|
656 s += "</tr>\n";
|
|
657 }
|
|
658 }
|
|
659
|
|
660 return s + "</table>";
|
|
661 }
|
|
662 }
|
|
663 }
|