* Makefile: Add reference to ICSharpCode.SharpZipLib.dll.
[mono.git] / mcs / tools / monodoc / Monodoc / ecma-provider.cs
1 //
2 // The provider for a tree of ECMA documents
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Joshua Tauberer (tauberer@for.net)
7 //
8 // (C) 2002, 2003 Ximian, Inc.
9 // (C) 2003 Joshua Tauberer.
10 //
11 // TODO:
12 //   Should cluster together constructors
13 //
14 // Easy:
15 //   Should render attributes on the signature.
16 //   Include examples as well.
17 //
18 namespace Monodoc {
19 using System;
20 using System.Diagnostics;
21 using System.Reflection;
22 using System.IO;
23 using System.Xml;
24 using System.Xml.XPath;
25 using System.Xml.Xsl;
26 using System.Text;
27 using System.Collections;
28 using Monodoc.Lucene.Net.Index;
29 using Monodoc.Lucene.Net.Documents;
30
31 using Mono.Documentation;
32
33 using BF = System.Reflection.BindingFlags;
34
35 //
36 // Helper routines to extract information from an Ecma XML document
37 //
38 public static class EcmaDoc {
39         public static string GetFullClassName (XmlDocument doc)
40         {
41                 return doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
42         }
43         
44         public static string GetClassName (XmlDocument doc)
45         {
46                 return GetDisplayName (doc.SelectSingleNode ("/Type")).Replace ("+", ".");
47         }
48
49         public static string GetDisplayName (XmlNode type)
50         {
51                 if (type.Attributes ["DisplayName"] != null) {
52                         return type.Attributes ["DisplayName"].Value;
53                 }
54                 return type.Attributes ["Name"].Value;
55         }
56
57         public static string GetClassAssembly (XmlDocument doc)
58         {
59                 return doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
60         }
61
62         public static string GetClassNamespace (XmlDocument doc)
63         {
64                 string s = doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
65
66                 return s.Substring (0, s.LastIndexOf ("."));
67         }
68         
69         public static string GetTypeKind (XmlDocument doc)
70         {
71                 XmlNode node = doc.SelectSingleNode ("/Type/Base/BaseTypeName");
72
73                 if (node == null){
74                         if (GetFullClassName (doc) == "System.Object")
75                                 return "Class";
76                         return "Interface";
77                 }
78
79                 switch (node.InnerText){
80
81                 case "System.Delegate":
82                 case "System.MulticastDelegate":
83                         return "Delegate";
84                 case "System.ValueType":
85                         return "Structure";
86                 case "System.Enum":
87                         return "Enumeration";
88                 default:
89                         return "Class";
90                 }
91         }
92
93         //
94         // Utility function: converts a fully .NET qualified type name into a C#-looking one
95         //
96         public static string ConvertCTSName (string type)
97         {
98                 if (!type.StartsWith ("System."))
99                         return type;
100
101                 if (type.EndsWith ("*"))
102                         return ConvertCTSName(type.Substring(0, type.Length - 1)) + "*";
103                 if (type.EndsWith ("&"))
104                         return ConvertCTSName(type.Substring(0, type.Length - 1)) + "&";
105                 if (type.EndsWith ("[]"))
106                         return ConvertCTSName(type.Substring(0, type.Length - 2)) + "[]";
107
108                 switch (type) {
109                 case "System.Byte": return "byte";
110                 case "System.SByte": return "sbyte";
111                 case "System.Int16": return "short";
112                 case "System.Int32": return "int";
113                 case "System.Int64": return "long";
114                         
115                 case "System.UInt16": return "ushort";
116                 case "System.UInt32": return "uint";
117                 case "System.UInt64": return "ulong";
118                         
119                 case "System.Single":  return "float";
120                 case "System.Double":  return "double";
121                 case "System.Decimal": return "decimal";
122                 case "System.Boolean": return "bool";
123                 case "System.Char":    return "char";
124                 case "System.String":  return "string";
125                         
126                 case "System.Object":  return "object";
127                 case "System.Void":  return "void";
128                 }
129
130                 if (type.LastIndexOf(".") == 6)
131                         return type.Substring(7);
132                 
133                 return type;
134         }
135
136         internal static string GetNamespaceFile (string dir, string ns)
137         {
138                 string nsxml = Path.Combine (dir, "ns-" + ns + ".xml");
139                 if (!File.Exists (nsxml))
140                         nsxml = Path.Combine (dir, ns + ".xml");
141                 return nsxml;
142         }
143 }
144
145 //
146 // The Ecma documentation provider:
147 //
148 // It populates a tree with contents during the help assembly
149 //
150 public class EcmaProvider : Provider {
151         ArrayList/*<string>*/ asm_dirs = new ArrayList ();
152
153         public EcmaProvider ()
154         {
155         }
156
157         public EcmaProvider (string base_directory)
158         {
159                 AddDirectory (base_directory);
160         }
161
162         public void AddDirectory (string directory)
163         {
164                 if (!Directory.Exists (directory))
165                         throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", directory));
166                 asm_dirs.Add (directory);
167         }
168         
169         public override void PopulateTree (Tree tree)
170         {
171                 ArrayList ns_dirs = new ArrayList ();
172                 foreach (string asm in asm_dirs) {
173                         ns_dirs.AddRange (Directory.GetDirectories (asm));
174                 }
175
176                 foreach (string ns in ns_dirs){
177                         string basedir = Directory.GetParent (ns).FullName;
178                         string [] files = Directory.GetFiles (ns);
179                         Node ns_node = null;
180                         string tn = null;
181                         
182                         Hashtable nsnodes = new Hashtable();
183
184                         foreach (string file in files){
185                                 if (!file.EndsWith (".xml"))
186                                         continue;
187
188                                 if (ns_node == null) {
189                                         tn = Path.GetFileName (ns);
190                                         tree.HelpSource.Message (TraceLevel.Info, "Processing namespace {0}", tn);
191                                         ns_node = tree.LookupNode (tn, "N:" + tn);
192                                         string ns_summary_file = EcmaDoc.GetNamespaceFile (basedir, tn);
193                                         
194                                         nsnodes[ns_node] = nsnodes;
195                                         
196                                         if (File.Exists (ns_summary_file)) {
197                                                 XmlDocument nsSummaryFile = new XmlDocument ();
198                                                 nsSummaryFile.Load (ns_summary_file);
199                                                 namespace_realpath [tn] = ns_summary_file;
200                                                 
201                                                 XmlNode ns_summary = nsSummaryFile.SelectSingleNode ("Namespace/Docs/summary");
202                                                 if (ns_summary != null && ns_summary.InnerText.Trim () != "To be added." && ns_summary.InnerText != "") {
203                                                         namespace_summaries [tn] = ns_summary;
204                                                         namespace_remarks [tn] = nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks");
205                                                 }
206                                                 
207                                         } else if (!namespace_summaries.ContainsKey (tn)) {
208                                                 namespace_summaries [tn] = null;
209                                                 namespace_remarks [tn] = null;
210                                         }
211                                 }
212                                 tree.HelpSource.Message (TraceLevel.Verbose, "    Processing input file {0}", Path.GetFileName (file));
213
214                                 PopulateClass (tree, tn, ns_node, file);
215                         }
216                         
217                         // Sort the list of types in each namespace
218                         foreach (Node ns_node2 in nsnodes.Keys)
219                                 ns_node2.Sort();
220                 }
221
222         }
223                 
224         struct TypeInfo : IComparable {
225                 public string type_assembly;
226                 public string type_name;
227                 public string type_full;
228                 public string type_kind;
229                 public XmlNode type_doc;
230
231                 public TypeInfo (string k, string a, string f, string s, XmlNode n)
232                 {
233                         type_assembly = a;
234                         type_name = s;
235                         type_doc = n;
236                         type_kind = k;
237                         type_full = f;
238                 }
239
240                 public int CompareTo (object b)
241                 {
242                         TypeInfo na = this;
243                         TypeInfo nb = (TypeInfo) b;
244
245                         return String.Compare (na.type_full, nb.type_full);
246                 }
247         }
248         
249         //
250         // Packs a file with the summary data
251         //
252         public override void CloseTree (HelpSource hs, Tree tree)
253         {
254                 foreach (DictionaryEntry de in class_summaries){
255                         XmlDocument doc = new XmlDocument ();
256                         string ns = (string) de.Key;
257                         
258                         ArrayList list = (ArrayList) de.Value;
259                         list.Sort();
260
261                         XmlElement elements = doc.CreateElement ("elements");
262                         doc.AppendChild (elements);
263                         
264                         if (namespace_summaries [ns] != null)
265                                 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_summaries [ns],true));
266                         else
267                                 elements.AppendChild (doc.CreateElement("summary"));
268                         
269                         if (namespace_remarks [ns] != null)
270                                 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_remarks [ns],true));
271                         else
272                                 elements.AppendChild (doc.CreateElement("remarks"));
273                         
274                         hs.Message (TraceLevel.Info, "Have {0} elements in the {1}", list.Count, ns);
275                         foreach (TypeInfo p in list){
276                                 XmlElement e = null;
277                                 
278                                 switch (p.type_kind){
279                                 case "Class":
280                                         e = doc.CreateElement ("class"); 
281                                         break;
282                                         
283                                 case "Enumeration":
284                                         e = doc.CreateElement ("enum");
285                                         break;
286                                         
287                                 case "Structure":
288                                         e = doc.CreateElement ("struct");
289                                         break;
290                                         
291                                 case "Delegate":
292                                         e = doc.CreateElement ("delegate");
293                                         break;
294                                         
295                                 case "Interface":
296                                         e = doc.CreateElement ("interface");
297                                         break;
298                                 }
299                                 
300                                 e.SetAttribute ("name", p.type_name);
301                                 e.SetAttribute ("fullname", p.type_full);
302                                 e.SetAttribute ("assembly", p.type_assembly);
303                                 XmlNode copy = doc.ImportNode (p.type_doc, true);
304                                 e.AppendChild (copy);
305                                 elements.AppendChild (e);
306                         }
307                         hs.PackXml ("xml.summary." + ns, doc,(string) namespace_realpath[ns]);
308                 }
309                 
310                 
311                 XmlDocument nsSummary = new XmlDocument ();
312                 XmlElement root = nsSummary.CreateElement ("elements");
313                 nsSummary.AppendChild (root);
314                 
315                 foreach (DictionaryEntry de in namespace_summaries) {
316                         XmlNode n = (XmlNode)de.Value;
317                         XmlElement summary = nsSummary.CreateElement ("namespace");
318                         summary.SetAttribute ("ns", (string)de.Key);
319                         root.AppendChild (summary);
320                         if (n != null)
321                                 summary.AppendChild (nsSummary.ImportNode (n,true));
322                         else
323                                 summary.AppendChild (nsSummary.CreateElement("summary"));
324                 }
325                 tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null);
326                 AddExtensionMethods (tree);
327         }
328
329         void AddExtensionMethods (Tree tree)
330         {
331                 XmlDocument extensions = null;
332                 XmlNode root = null;
333                 int numMethods = 0;
334                 foreach (string asm in asm_dirs) {
335                         string overview_file = Path.Combine (asm, "index.xml");
336                         if (File.Exists (overview_file)) {
337                                 XmlDocument overview = new XmlDocument ();
338                                 overview.Load (overview_file);
339                                 XmlNodeList e = overview.SelectNodes ("/Overview/ExtensionMethods/*");
340                                 if (e.Count > 0) {
341                                         if (extensions == null) {
342                                                 extensions = new XmlDocument ();
343                                                 root = extensions.CreateElement ("ExtensionMethods");
344                                                 extensions.AppendChild (root);
345                                         }
346                                         foreach (XmlNode n in e) {
347                                                 ++numMethods;
348                                                 root.AppendChild (extensions.ImportNode (n, true));
349                                         }
350                                 }
351                         }
352                 }
353                 if (extensions != null) {
354                         tree.HelpSource.Message (TraceLevel.Info, "Have {0} extension methods", numMethods);
355                         tree.HelpSource.PackXml ("ExtensionMethods.xml", extensions, "ExtensionMethods.xml");
356                 }
357         }
358                
359         Hashtable class_summaries = new Hashtable ();
360         Hashtable namespace_summaries = new Hashtable ();
361         Hashtable namespace_remarks = new Hashtable ();
362         Hashtable namespace_realpath = new Hashtable ();
363         XmlDocument doc;
364         
365         void PopulateClass (Tree tree, string ns, Node ns_node, string file)
366         {
367                 doc = new XmlDocument ();
368                 doc.Load (file);
369                 
370                 string name = EcmaDoc.GetClassName (doc);
371                 string assembly = EcmaDoc.GetClassAssembly (doc);
372                 string kind = EcmaDoc.GetTypeKind (doc);
373                 string full = EcmaDoc.GetFullClassName (doc);
374
375                 Node class_node;
376                 string file_code = ns_node.tree.HelpSource.PackFile (file);
377
378                 XmlNode class_summary = doc.SelectSingleNode ("/Type/Docs/summary");
379                 ArrayList l = (ArrayList) class_summaries [ns];
380                 if (l == null){
381                         l = new ArrayList ();
382                         class_summaries [ns] = (object) l;
383                 }
384                 l.Add (new TypeInfo (kind, assembly, full, name, class_summary));
385                
386                 class_node = ns_node.LookupNode (String.Format ("{0} {1}", name, kind), "ecma:" + file_code + "#" + name + "/");
387                 
388                 if (kind == "Delegate") {
389                         if (doc.SelectSingleNode("/Type/ReturnValue") == null)
390                                 tree.HelpSource.Message (TraceLevel.Error, "Delegate " + name + " does not have a ReturnValue node.  See the ECMA-style updates.");
391                 }
392
393                 if (kind == "Enumeration")
394                         return;
395
396                 if (kind == "Delegate")
397                         return;
398                 
399                 //
400                 // Always add the Members node
401                 //
402                 class_node.CreateNode ("Members", "*");
403
404                 PopulateMember (name, class_node, "Constructor", "Constructors");
405                 PopulateMember (name, class_node, "Method", "Methods");
406                 PopulateMember (name, class_node, "Property", "Properties");
407                 PopulateMember (name, class_node, "Field", "Fields");
408                 PopulateMember (name, class_node, "Event", "Events");
409                 PopulateMember (name, class_node, "Operator", "Operators");
410         }
411
412         class NodeIndex {
413                 public XmlNode node;
414                 public int     index;
415
416                 public NodeIndex (XmlNode node, int index)
417                 {
418                         this.node = node;
419                         this.index = index;
420                 }
421         }
422
423         struct NodeCompare : IComparer {
424                 public int Compare (object a, object b)
425                 {
426                         NodeIndex na = (NodeIndex) a;
427                         NodeIndex nb = (NodeIndex) b;
428
429                         return String.Compare (na.node.Attributes ["MemberName"].InnerText,
430                                                nb.node.Attributes ["MemberName"].InnerText);
431                 }
432         }
433
434         string GetMemberName (XmlNode node)
435         {
436                 return node.Attributes ["MemberName"].InnerText;
437         }
438         
439         //
440         // Performs an XPath query on the document to extract the nodes for the various members
441         // we also use some extra text to pluralize the caption
442         //
443         void PopulateMember (string typename, Node node, string type, string caption)
444         {
445                 string select = type;
446                 if (select == "Operator") select = "Method";
447                 
448                 XmlNodeList list1 = doc.SelectNodes (String.Format ("/Type/Members/Member[MemberType=\"{0}\"]", select));
449                 ArrayList list = new ArrayList();
450                 int i = 0;
451                 foreach (XmlElement n in list1) {
452                         n.SetAttribute("assembler_index", (i++).ToString());
453                         if (type == "Method" && GetMemberName(n).StartsWith("op_")) continue;
454                         if (type == "Operator" && !GetMemberName(n).StartsWith("op_")) continue;
455                         list.Add(n);
456                 }
457                 
458                 int count = list.Count;
459                 
460                 if (count == 0)
461                         return;
462
463                 Node nodes_node;
464                 string key = type.Substring (0, 1);
465                 nodes_node = node.CreateNode (caption, key);
466                 
467                 switch (type) {
468                         case "Event":
469                         case "Field":
470                                 foreach (XmlElement n in list)
471                                         nodes_node.CreateNode (GetMemberName (n), n.GetAttribute("assembler_index"));
472                                 break;
473
474                         case "Constructor":
475                                 foreach (XmlElement n in list)
476                                         nodes_node.CreateNode (EcmaHelpSource.MakeSignature(n, typename), n.GetAttribute("assembler_index"));
477                                 break;
478
479                         case "Property": // properties with indexers can be overloaded too
480                         case "Method":
481                         case "Operator":
482                                 foreach (XmlElement n in list) {
483                                         bool multiple = false;
484                                         foreach (XmlNode nn in list) {
485                                                 if (n != nn && GetMemberName(n) == nn.Attributes ["MemberName"].InnerText) {
486                                                         multiple = true;
487                                                         break;
488                                                 }
489                                         }
490                                         
491                                         string group, name, sig;
492                                         if (type != "Operator") {
493                                                 name = GetMemberName(n);
494                                                 sig = EcmaHelpSource.MakeSignature(n, null);
495                                                 group = name;
496                                         } else {
497                                                 EcmaHelpSource.MakeOperatorSignature(n, out name, out sig);
498                                                 group = name;
499                                         }
500                                         
501                                         if (multiple) {
502                                                 nodes_node.LookupNode (group, group)
503                                                         .CreateNode (sig, n.GetAttribute("assembler_index"));
504                                         } else {
505                                                 nodes_node.CreateNode (name, n.GetAttribute("assembler_index"));
506                                         }
507                                 }
508                                 
509                                 foreach (Node n in nodes_node.Nodes) {
510                                         if (!n.IsLeaf)
511                                                 n.Sort ();
512                                 }
513                                 
514                                 break;
515                                 
516                         default:
517                                 throw new InvalidOperationException();
518                 }
519                 
520                 nodes_node.Sort ();
521         }
522
523 }
524
525 //
526 // The Ecma HelpSource provider
527 //
528 public class EcmaHelpSource : HelpSource {
529
530         public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
531         {
532                 ExtObject = new ExtensionObject (this);
533         }
534
535         public EcmaHelpSource ()
536         {
537                 ExtObject = new ExtensionObject (this);
538         }
539
540         static string css_ecma;
541         public static string css_ecma_code {
542                 get {
543                         if (css_ecma != null)
544                                 return css_ecma;
545                         if (use_css) {
546                                 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
547                                 Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
548                                 css_ecma = (new StreamReader (str_css)).ReadToEnd();
549                         } else {
550                                 css_ecma = String.Empty;
551                         }
552                         return css_ecma;
553                 }
554         }
555
556         public override string InlineCss {
557                 get {return base.InlineCss + css_ecma_code;}
558         }
559
560         static string js;
561         public static string js_code {
562                 get {
563                         if (js != null)
564                                 return js;
565                         if (use_css) {
566                                 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
567                                 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
568                                 js = (new StreamReader (str_js)).ReadToEnd();
569                         } else {
570                                 js = String.Empty;
571                         }
572                         return js;
573                 }
574         }
575
576         public override string InlineJavaScript {
577                 get {return js_code + base.InlineJavaScript;}
578         }
579
580         public override string GetText (string url, out Node match_node)
581         {
582                 match_node = null;
583
584                 string c = GetCachedText (url);
585                 if (c != null)
586                         return c;
587
588                 string cached = GetCachedText (url);
589                 if (cached != null)
590                         return cached;
591                 
592                 if (url == "root:")
593                 {
594                         XmlReader summary = GetHelpXml ("mastersummary.xml");
595                         if (summary == null)
596                                 return null;
597
598                         XsltArgumentList args = new XsltArgumentList();
599                         args.AddExtensionObject("monodoc:///extensions", ExtObject);
600                         args.AddParam("show", "", "masteroverview");
601                         string s = Htmlize(new XPathDocument (summary), args);
602                         return BuildHtml (css_ecma_code, js_code, s); 
603                 }
604                 
605                 if (url.StartsWith ("ecma:")) {
606                         string s = GetTextFromUrl (url);
607                         return BuildHtml (css_ecma_code, js_code, s); 
608                 }
609
610                 return null;
611         }
612
613
614         string RenderMemberLookup (string typename, string member, ref Node type_node)
615         {
616                 if (type_node.Nodes == null)
617                         return null;
618
619                 string membername = member;
620                 string[] argtypes = null;
621                 if (member.IndexOf("(") > 0) {
622                         membername = membername.Substring(0, member.IndexOf("("));
623                         member = member.Replace("@", "&");
624                         
625                         // reform the member signature with CTS names
626
627                         string x = member.Substring(member.IndexOf("(")+1);
628                         argtypes = x.Substring(0, x.Length-1).Split(',', ':'); // operator signatures have colons
629
630                         if (membername == ".ctor")
631                                 membername = typename;
632
633                         member = membername + "(";
634                         for (int i = 0; i < argtypes.Length; i++) {
635                                 argtypes[i] = EcmaDoc.ConvertCTSName(argtypes[i]);
636                                 if (i > 0) member += ",";
637                                 member += argtypes[i];
638                         }
639                         member += ")";
640                 }
641                 
642                 // Check if a node caption matches exactly
643                 
644                 bool isoperator = false;
645                 
646                 if ((membername == "op_Implicit" || membername == "op_Explicit") && argtypes.Length == 2) {
647                         isoperator = true;
648                         membername = "Conversion";
649                         member = argtypes[0] + " to " + argtypes[1];
650                 } else if (membername.StartsWith("op_")) {
651                         isoperator = true;
652                         membername = membername.Substring(3);
653                 }
654
655                 foreach (Node x in type_node.Nodes){
656                         if (x.Nodes == null)
657                                 continue;
658                         if (isoperator && x.Caption != "Operators")
659                                 continue;
660                         
661                         foreach (Node m in x.Nodes) {
662                                 string caption = m.Caption;
663                                 string ecaption = ToEscapedMemberName (caption);
664                                 if (m.IsLeaf) {
665                                         // No overloading (usually), is just the member name.  The whole thing for constructors.
666                                         if (caption == membername || caption == member ||
667                                                         ecaption == membername || ecaption == member) {
668                                                 type_node = m;
669                                                 return GetTextFromUrl (m.URL);
670                                         }
671                                 } else if (caption == member || ecaption == member) {
672                                         // Though there are overloads, no arguments are in the url, so use this base node
673                                         type_node = m;
674                                         return GetTextFromUrl (m.URL);
675                                 } else {
676                                         // Check subnodes which are the overloads -- must match signature
677                                         foreach (Node mm in m.Nodes) {
678                                                 ecaption = ToEscapedTypeName (mm.Caption);
679                                                 if (mm.Caption == member || ecaption == member) {
680                                                         type_node = mm;
681                                                         return GetTextFromUrl (mm.URL);
682                                                 }
683                                         }
684                                 }
685                         }
686                 }
687                 
688                 return null;
689         }
690
691         public static string MakeSignature (XmlNode n, string cstyleclass)
692         {
693                 string sig;
694
695                 if (cstyleclass == null)
696                         sig = n.Attributes["MemberName"].InnerText;
697                 else {
698                         // constructor style
699                         sig = cstyleclass;
700                 }
701         
702                 /*if (n.SelectSingleNode("MemberType").InnerText == "Method" || n.SelectSingleNode("MemberType").InnerText == "Constructor") {*/ // properties with indexers too
703                         XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
704                         sig += "(";
705                         bool first = true;
706                         foreach (XmlNode p in paramnodes) {
707                                 if (!first) sig += ",";
708                                 string type = p.Attributes["Type"].InnerText;
709                                 type = EcmaDoc.ConvertCTSName(type);
710                                 sig += type;
711                                 first = false;
712                         }
713                         sig += ")";
714                 //}
715
716                 return sig;
717         }
718         
719         public static void MakeOperatorSignature (XmlNode n, out string nicename, out string sig)
720         {
721                 string name;
722
723                 name = n.Attributes["MemberName"].InnerText;
724                 nicename = name.Substring(3);
725                 
726                 switch (name) {
727                         // unary operators: no overloading possible
728                         case "op_UnaryPlus": case "op_UnaryNegation": case "op_LogicalNot": case "op_OnesComplement":
729                         case "op_Increment": case "op_Decrement": 
730                         case "op_True": case "op_False":
731                                 sig = nicename;
732                                 break;
733                         
734                         // binary operators: overloading is possible based on parameter types
735                         case "op_Addition": case "op_Subtraction": case "op_Multiply": case "op_Division": case "op_Modulus":
736                         case "op_BitwiseAnd": case "op_BitwiseOr": case "op_ExclusiveOr":
737                         case "op_LeftShift": case "op_RightShift":
738                         case "op_Equality": case "op_Inequality":
739                         case "op_GreaterThan": case "op_LessThan": case "op_GreaterThanOrEqual": case "op_LessThanOrEqual":
740                                 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
741                                 sig = nicename + "(";
742                                 bool first = true;
743                                 foreach (XmlNode p in paramnodes) {
744                                         if (!first) sig += ",";
745                                         string type = p.Attributes["Type"].InnerText;
746                                         type = EcmaDoc.ConvertCTSName(type);
747                                         sig += type;
748                                         first = false;
749                                 }
750                                 sig += ")";
751                                 break;
752                         
753                         // overloading based on parameter and return type
754                         case "op_Implicit": case "op_Explicit":
755                                 nicename = "Conversion";
756                                 string arg = n.SelectSingleNode("Parameters/Parameter/@Type").InnerText;
757                                 string ret = n.SelectSingleNode("ReturnValue/ReturnType").InnerText;
758                                 sig = EcmaDoc.ConvertCTSName(arg) + " to " + EcmaDoc.ConvertCTSName(ret);
759                                 break;
760                                 
761                         default:
762                                 throw new InvalidOperationException();
763                 }       
764         }
765
766         //
767         // This routine has to perform a lookup on a type.
768         //
769         // Example: T:System.Text.StringBuilder
770         //
771         // The prefix is the kind of opereation being requested (T:, E:, M: etc)
772         // ns is the namespace being looked up
773         // type is the type being requested
774         //
775         // This has to walk our toplevel (which is always a namespace)
776         // And then the type space, and then walk further down depending on the request
777         //
778         public override string RenderTypeLookup (string prefix, string ns, string type, string member, out Node match_node)
779         {
780                 string url = GetUrlForType (prefix, ns, type, member, out match_node);
781                 if (url == null) return null;
782                 return GetTextFromUrl (url);
783         }
784
785         public virtual string GetIdFromUrl (string prefix, string ns, string type)
786         {
787                 Node tmp_node = new Node (Tree, "", "");
788                 string url = GetUrlForType (prefix, ns, type, null, out tmp_node);
789                 if (url == null) return null;
790                 return GetFile (url.Substring (5), out url);
791         }
792         
793         public string GetUrlForType (string prefix, string ns, string type, string member, out Node match_node)
794         {
795                 if (!prefix.EndsWith(":"))
796                         throw new ArgumentException("prefix");
797
798                 if (member != null)
799                         member = member.Replace ("#", ".").Replace ("{", "<").Replace ("}", ">");
800                         
801                 // If a nested type, compare only inner type name to node list.
802                 // This should be removed when the node list doesn't lose the containing type name.
803                 type = ToEscapedTypeName (type.Replace("+", "."));
804                 MatchAttempt[] attempts = GetAttempts (ns, type);
805
806                 foreach (Node ns_node in Tree.Nodes){
807                         string ns_node_namespace = ns_node.Element.Substring (2);
808
809                         if (!MatchesNamespace (attempts, ns_node_namespace, out type))
810                                 continue;
811                         
812                         foreach (Node type_node in ns_node.Nodes){
813                                 string element = type_node.Element;
814                                 
815                                 string cname;
816                                 if (element.StartsWith("T:")) {
817                                         cname = element.Substring(2);
818                                         string _ns;
819                                         RootTree.GetNamespaceAndType (cname, out _ns, out cname);
820                                         cname = ToEscapedTypeName (cname);
821                                         int pidx = cname.LastIndexOf (".");
822                                         cname = cname.Substring(pidx+1);
823                                         pidx = cname.LastIndexOf ("/");
824                                         if (pidx != -1)
825                                                 cname = cname.Substring(0, pidx);
826                                         cname = cname.Replace("+", ".");
827                                 } else {                                
828                                         int pidx = element.IndexOf ("#");
829                                         int sidx = element.IndexOf ("/");
830                                         cname = element.Substring (pidx + 1, sidx-pidx-1);
831                                         cname = ToEscapedTypeName (cname);
832                                 }
833                                 
834                                 //Console.WriteLine ("t:{0} cn:{1} p:{2}", type, cname, prefix);
835
836                                 if (type == cname && prefix == "T:") {
837                                         match_node = type_node;
838                                         return type_node.URL;
839                                 } else if (type.StartsWith (cname)){
840                                         int p = cname.Length;
841
842                                         match_node = type_node;
843                                         if (type == cname){
844                                                 string ret = RenderMemberLookup (type, member, ref match_node);
845                                                 if (ret == null)
846                                                         return type_node.URL;
847                                                 return match_node.URL;
848
849                                         } else if (type [p] == '/'){
850                                                 //
851                                                 // This handles summaries
852                                                 //
853                                                 match_node = null;
854                                                 foreach (Node nd in type_node.Nodes) {
855                                                         if (nd.Element [nd.Element.Length - 1] == type [p + 1]) {
856                                                                 match_node = nd;
857                                                                 break;
858                                                         }
859                                                 }
860                                                 
861                                                 string ret = type_node.URL;
862                                                 if (!ret.EndsWith("/")) ret += "/";
863                                                 return ret + type.Substring (p + 1);
864                                         }
865                                 }
866                         }
867                 }
868
869                 match_node = null;
870                 return null;
871         }
872
873         struct MatchAttempt {
874                 public string Namespace;
875                 public string Type;
876
877                 public MatchAttempt (string ns, string t)
878                 {
879                         Namespace = ns;
880                         Type = t;
881                 }
882         }
883
884         private static MatchAttempt[] GetAttempts (string ns, string type)
885         {
886                 MatchAttempt[] attempts = new MatchAttempt [Count (ns, '.') + 1];
887                 int wns = 0;
888                 for (int i = 0; i < ns.Length; ++i) {
889                         if (ns [i] == '.')
890                                 attempts [wns++] = new MatchAttempt (ns.Substring (0, i), 
891                                                 ns.Substring (i+1) + "." + type);
892                 }
893                 attempts [wns++] = new MatchAttempt (ns, type);
894                 return attempts;
895         }
896
897         private static int Count (string s, char c)
898         {
899                 int n = 0;
900                 for (int i = 0; i < s.Length; ++i)
901                         if (s [i] == c)
902                                 ++n;
903                 return n;
904         }
905
906         private static bool MatchesNamespace (MatchAttempt[] attempts, string ns, out string type)
907         {
908                 for (int i = 0; i < attempts.Length; ++i)
909                         if (ns == attempts [i].Namespace) {
910                                 type = attempts [i].Type;
911                                 return true;
912                         }
913                 type = null;
914                 return false;
915         }
916         
917         public static string ToEscapedTypeName (string typename)
918         {
919                 return ToEscapedName (typename, "`");
920         }
921
922         static string ToEscapedName (string name, string escape)
923         {
924                 StringBuilder filename = new StringBuilder (name.Length);
925                 int numArgs = 0;
926                 int numLt = 0;
927                 bool copy = true;
928
929                 for (int i = 0; i < name.Length; ++i) {
930                         char c = name [i];
931                         switch (c) {
932                                 case '{':
933                                 case '<':
934                                         copy = false;
935                                         ++numLt;
936                                         break;
937                                 case '}':
938                                 case '>':
939                                         --numLt;
940                                         if (numLt == 0) {
941                                                 filename.Append (escape).Append ((numArgs+1).ToString());
942                                                 numArgs = 0;
943                                                 copy = true;
944                                         }
945                                         break;
946                                 case ',':
947                                         if (numLt == 1)
948                                                 ++numArgs;
949                                         break;
950                                 default:
951                                         if (copy)
952                                                 filename.Append (c);
953                                         break;
954                         }
955                 }
956                 return filename.ToString ();
957         }
958
959         static string ToEscapedMemberName (string membername)
960         {
961                 return ToEscapedName (membername, "``");
962         }
963         
964         public override string GetNodeXPath (XPathNavigator n)
965         {
966                 if (n.Matches ("/Type/Docs/param")) {
967                         string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
968                         string param_name = (string) n.Evaluate ("string (@name)");
969                         
970                         return String.Format ("/Type [@FullName = '{0}']/Docs/param[@name='{1}']", type_name, param_name);
971                 }
972
973                 if (n.Matches ("/Type/Docs/*")) {
974                         string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
975                         
976                         return String.Format ("/Type [@FullName = '{0}']/Docs/{1}", type_name, n.Name);
977                 }
978                 
979                 if (n.Matches ("/Type/Members/Member/Docs/*")) {
980                         string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
981                         string member_name = (string) n.Evaluate ("string (ancestor::Member/@MemberName)");
982                         string member_sig = (string) n.Evaluate ("string (ancestor::Member/MemberSignature [@Language='C#']/@Value)");
983                         string param_name = (string) n.Evaluate ("string (@name)");
984                         
985                         if (param_name == null || param_name == "") {
986                                 return String.Format (
987                                 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/{3}",
988                                 type_name, member_name, member_sig, n.Name);
989                         } else {
990                                 return String.Format (
991                                 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/param [@name = '{3}']",
992                                 type_name, member_name, member_sig, param_name);
993                         }
994                 }
995                 
996                 Message (TraceLevel.Warning, "WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
997                 return base.GetNodeXPath (n);
998         }
999
1000         protected virtual XmlDocument GetNamespaceDocument (string ns)
1001         {
1002                 return GetHelpXmlWithChanges ("xml.summary." + ns);
1003         }
1004
1005         public override string RenderNamespaceLookup (string nsurl, out Node match_node)
1006         {
1007                 foreach (Node ns_node in Tree.Nodes){
1008                         if (ns_node.Element != nsurl)
1009                                 continue;
1010
1011                         match_node = ns_node;
1012                         string ns_name = nsurl.Substring (2);
1013                         
1014                         XmlDocument doc = GetNamespaceDocument (ns_name);
1015                         if (doc == null)
1016                                 return null;
1017
1018                         XsltArgumentList args = new XsltArgumentList();
1019                         args.AddExtensionObject("monodoc:///extensions", ExtObject);
1020                         args.AddParam("show", "", "namespace");
1021                         args.AddParam("namespace", "", ns_name);
1022                         string s = Htmlize(doc, args);
1023                         return BuildHtml (css_ecma_code, js_code, s); 
1024
1025                 }
1026                 match_node = null;
1027                 return null;
1028         }
1029
1030         private string SelectString(XmlNode node, string xpath) {
1031                 XmlNode ret = node.SelectSingleNode(xpath);
1032                 if (ret == null) return "";
1033                 return ret.InnerText;
1034         }
1035         private int SelectCount(XmlNode node, string xpath) {
1036                 return node.SelectNodes(xpath).Count;
1037         }
1038
1039         //
1040         // Returns the XmlDocument from the given url, and fills in `rest'
1041         //
1042         protected virtual XmlDocument GetXmlFromUrl(string url, out string rest) {
1043                 // Remove ecma:
1044                 url = url.Substring (5);
1045                 string file = GetFile (url, out rest);
1046
1047                 // Console.WriteLine ("Got [{0}] and [{1}]", file, rest);
1048                 return GetHelpXmlWithChanges (file);
1049         }
1050         
1051         string GetTextFromUrl (string url)
1052         {
1053                 string rest, rest2;
1054                 Node node;
1055
1056                 XmlDocument doc = GetXmlFromUrl (url, out rest);
1057                 
1058                 // Load base-type information so the stylesheet can draw the inheritance
1059                 // tree and (optionally) include inherited members in the members list.
1060                 XmlElement basenode = (XmlElement)doc.SelectSingleNode("Type/Base");
1061                 XmlElement membersnode = (XmlElement)doc.SelectSingleNode("Type/Members");
1062                 XmlNode basetype = doc.SelectSingleNode("Type/Base/BaseTypeName");
1063                 int baseindex = 0;
1064                 while (basetype != null) {
1065                         // Add the ParentType node to Type/Base
1066                         XmlElement inheritancenode = doc.CreateElement("ParentType");
1067                         inheritancenode.SetAttribute("Type", basetype.InnerText);
1068                         inheritancenode.SetAttribute("Order", (baseindex++).ToString());
1069                         basenode.AppendChild(inheritancenode);
1070                         
1071                         // Load the base type XML data
1072                         int dot = basetype.InnerText.LastIndexOf('.');
1073                         string ns = basetype.InnerText.Substring(0, dot == -1 ? 0 : dot);
1074                         string n = basetype.InnerText.Substring(dot == -1 ? 0 : dot+1);
1075                         string basetypeurl = GetUrlForType("T:", ns, n, null, out node);
1076                         XmlDocument basetypedoc = null;
1077                         if (basetypeurl != null)
1078                                 basetypedoc = GetXmlFromUrl (basetypeurl, out rest2);
1079                         if (basetypedoc == null) {
1080                                 inheritancenode.SetAttribute("Incomplete", "1");
1081                                 break;
1082                         }
1083                         
1084                         if (SettingsHandler.Settings.ShowInheritedMembers) {
1085                                 // Add inherited members
1086                                 foreach (XmlElement member in basetypedoc.SelectNodes("Type/Members/Member[not(MemberType='Constructor')]")) {
1087                                         string sig = SelectString(member, "MemberSignature[@Language='C#']/@Value");
1088                                         if (sig.IndexOf(" static ") >= 0) continue;
1089                                         
1090                                         // If the signature of member matches the signature of a member already in the XML,
1091                                         // don't add it.
1092                                         string xpath = "@MemberName='" + SelectString(member, "@MemberName") + "'";
1093                                         xpath       += " and ReturnValue/ReturnType='" + SelectString(member, "ReturnValue/ReturnType") + "'";
1094                                         xpath       += " and count(Parameters/Parameter)=" + SelectCount(member, "Parameters/Parameter") + "";
1095                                         int pi = 1;
1096                                         foreach (XmlElement p in member.SelectNodes("Parameters/Parameter")) {
1097                                                 xpath   += " and Parameters/Parameter[position()=" + pi + "]/@Type = '" + p.GetAttribute("Type") + "'";
1098                                                 pi++;
1099                                         }
1100                                         
1101                                         // If a signature match is found, don't add.
1102                                         XmlNode match = membersnode.SelectSingleNode("Member[" + xpath + "]");
1103                                         if (match != null)
1104                                                 continue;
1105                                         
1106                                         member.SetAttribute("DeclaredIn", basetype.InnerText);
1107                                         membersnode.AppendChild(membersnode.OwnerDocument.ImportNode(member, true));                            
1108                                 }
1109                         }
1110                         
1111                         basetype = basetypedoc.SelectSingleNode("Type/Base/BaseTypeName");
1112                 }
1113                 ArrayList extensions = new ArrayList ();
1114                 foreach (HelpSource hs in RootTree.HelpSources) {
1115                         EcmaHelpSource es = hs as EcmaHelpSource;
1116                         if (es == null)
1117                                 continue;
1118                         Stream s = es.GetHelpStream ("ExtensionMethods.xml");
1119                         if (s != null) {
1120                                 XmlDocument d = new XmlDocument ();
1121                                 d.Load (s);
1122                                 foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) {
1123                                         extensions.Add (n);
1124                                 }
1125                         }
1126                 }
1127                 XmlDocUtils.AddExtensionMethods (doc, extensions, delegate (string s) {
1128                                 s = s.StartsWith ("T:") ? s : "T:" + s;
1129                                 return RootTree.GetHelpXml (s);
1130                 });
1131
1132                 XsltArgumentList args = new XsltArgumentList();
1133
1134                 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1135                 
1136                 if (rest == "") {
1137                         args.AddParam("show", "", "typeoverview");
1138                         string s = Htmlize(doc, args);
1139                         return BuildHtml (css_ecma_code, js_code, s); 
1140                 }
1141                 
1142                 string [] nodes = rest.Split (new char [] {'/'});
1143                 
1144                 switch (nodes.Length) {
1145                         case 1:
1146                                 args.AddParam("show", "", "members");
1147                                 args.AddParam("index", "", "all");
1148                                 break;
1149                         case 2:
1150                                 // Could either be a single member, or an overload thingy
1151                                 try {
1152                                         int.Parse (nodes [1]); // is it an int
1153                                         
1154                                         args.AddParam("show", "", "member");
1155                                         args.AddParam("index", "", nodes [1]);
1156                                 } catch {
1157                                         args.AddParam("show", "", "overloads");
1158                                         args.AddParam("index", "", nodes [1]);
1159                                 }
1160                                 break;
1161                         case 3:
1162                                 args.AddParam("show", "", "member");
1163                                 args.AddParam("index", "", nodes [2]);
1164                                 break;
1165                         default:
1166                                 return "What the hell is this URL " + url;
1167                 }
1168
1169                 switch (nodes [0]){
1170                 case "C":
1171                         args.AddParam("membertype", "", "Constructor");
1172                         break;
1173                 case "M":
1174                         args.AddParam("membertype", "", "Method");
1175                         break;
1176                 case "P":
1177                         args.AddParam("membertype", "", "Property");
1178                         break;
1179                 case "F":
1180                         args.AddParam("membertype", "", "Field");
1181                         break;
1182                 case "E":
1183                         args.AddParam("membertype", "", "Event");
1184                         break;
1185                 case "O":
1186                         args.AddParam("membertype", "", "Operator");
1187                         break;
1188                 case "X":
1189                         args.AddParam("membertype", "", "ExtensionMethod");
1190                         break;
1191                 case "*":
1192                         args.AddParam("membertype", "", "All");
1193                         break;
1194                 default:
1195                         return "Unknown url: " + url;
1196                 }
1197
1198                 string html = Htmlize(doc, args);
1199                 return BuildHtml (css_ecma_code, js_code, html); 
1200         }
1201
1202         
1203         public override void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
1204         {
1205                 XsltArgumentList args = new XsltArgumentList ();
1206                 args.AddExtensionObject ("monodoc:///extensions", ExtObject);
1207                 
1208                 Htmlize (newNode, args, writer);
1209         }
1210
1211         static XslTransform ecma_transform;
1212
1213         public string Htmlize (IXPathNavigable ecma_xml)
1214         {
1215                 return Htmlize(ecma_xml, null);
1216         }
1217
1218         public string Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args)
1219         {
1220                 EnsureTransform ();
1221                 
1222                 StringWriter output = new StringWriter ();
1223                 ecma_transform.Transform (ecma_xml, args, output, CreateDocumentResolver ());
1224                 return output.ToString ();
1225         }
1226
1227         protected virtual XmlResolver CreateDocumentResolver ()
1228         {
1229                 // results in using XmlUrlResolver
1230                 return null;
1231         }
1232         
1233         static void Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args, XmlWriter w)
1234         {
1235                 EnsureTransform ();
1236                 
1237                 if (ecma_xml == null)
1238                         return;
1239
1240                 ecma_transform.Transform (ecma_xml, args, w, null);
1241         }
1242         
1243         static XslTransform ecma_transform_css, ecma_transform_no_css;
1244         static void EnsureTransform ()
1245         {
1246                 if (ecma_transform == null) {
1247                         ecma_transform_css = new XslTransform ();
1248                         ecma_transform_no_css = new XslTransform ();
1249                         Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
1250                         
1251                         Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
1252                         XmlReader xml_reader = new XmlTextReader (stream);
1253                         XmlResolver r = new ManifestResourceResolver (".");
1254                         ecma_transform_css.Load (xml_reader, r, null);
1255                         
1256                         stream = assembly.GetManifestResourceStream ("mono-ecma.xsl");
1257                         xml_reader = new XmlTextReader (stream);
1258                         ecma_transform_no_css.Load (xml_reader, r, null);
1259                 }
1260                 if (use_css)
1261                         ecma_transform = ecma_transform_css;
1262                 else
1263                         ecma_transform = ecma_transform_no_css;
1264         }
1265
1266         // This ExtensionObject stuff is used to check at run time whether
1267         // types and members have been implemented and whether there are any
1268         // MonoTODO attributes left on them. 
1269
1270         public readonly ExtensionObject ExtObject;
1271         public class ExtensionObject {
1272                 readonly EcmaHelpSource hs;
1273
1274                 //
1275                 // We are setting this to quiet now, as we need to transition
1276                 // monodoc to run with the 2.x runtime and provide accurate
1277                 // information in those cases.
1278                 //
1279                 bool quiet = true;
1280                 
1281                 public ExtensionObject (EcmaHelpSource hs)
1282                 {
1283                         this.hs = hs;
1284                 }
1285                 
1286                 public string Colorize(string code, string lang) {
1287                         return(Mono.Utilities.Colorizer.Colorize(code,lang));
1288                 }
1289                 // Used by stylesheet to nicely reformat the <see cref=> tags. 
1290                 public string MakeNiceSignature(string sig, string contexttype)
1291                 {
1292                         if (sig.Length < 3)
1293                                 return sig;
1294                         if (sig[1] != ':')
1295                                 return sig;
1296
1297                         char s = sig[0];
1298                         sig = sig.Substring(2);
1299                         
1300                         switch (s) {
1301                                 case 'N': return sig;
1302                                 case 'T': return ShortTypeName(sig, contexttype);
1303
1304                                 case 'C': case 'M': case 'P': case 'F': case 'E':
1305                                         string type, mem, arg;
1306                                         
1307                                         // Get arguments
1308                                         int paren;
1309                                         if (s == 'C' || s == 'M')
1310                                                 paren = sig.IndexOf("(");
1311                                         else if (s == 'P')
1312                                                 paren = sig.IndexOf("[");
1313                                         else
1314                                                 paren = 0;
1315                                         
1316                                         if (paren > 0 && paren < sig.Length-1) {
1317                                                 string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');                                          
1318                                                 for (int i = 0; i < args.Length; i++)
1319                                                         args[i] = ShortTypeName(args[i], contexttype);
1320                                                 arg = "(" + String.Join(", ", args) + ")";
1321                                                 sig = sig.Substring(0, paren); 
1322                                         } else {
1323                                                 arg = "";
1324                                         }
1325
1326                                         // Get type and member names
1327                                         int dot = sig.LastIndexOf(".");
1328                                         if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
1329                                                 mem = "";
1330                                                 type = sig;
1331                                         } else {
1332                                                 type = sig.Substring(0, dot);
1333                                                 mem = sig.Substring(dot);
1334                                         }
1335                                                 
1336                                         type = ShortTypeName(type, contexttype);
1337                                         
1338                                         return type + mem + arg;
1339
1340                                 default:
1341                                         return sig;
1342                         }
1343                 }
1344                 
1345                 public string EditUrl (XPathNodeIterator itr)
1346                 {
1347                         if (itr.MoveNext ())
1348                                 return hs.GetEditUri (itr.Current);
1349                         
1350                         return "";
1351                 }
1352
1353                 public string EditUrlNamespace (XPathNodeIterator itr, string ns, string section)
1354                 {
1355                         if (hs is EcmaUncompiledHelpSource)
1356                                 return "edit:file:" + Path.Combine(((EcmaUncompiledHelpSource)hs).BasePath, ns + ".xml") + "@/Namespace/Docs/" + section; 
1357                         else if (itr.MoveNext ())
1358                                 return EditingUtils.FormatEditUri(itr.Current.BaseURI, "/elements/" + section);
1359                         return "";
1360                 }
1361
1362                 private static string ShortTypeName(string name, string contexttype)
1363                 {
1364                         int dot = contexttype.LastIndexOf(".");
1365                         if (dot < 0) return name;
1366                         string contextns = contexttype.Substring(0, dot+1);
1367
1368                         if (name == contexttype)
1369                                 return name.Substring(dot+1);
1370                         
1371                         if (name.StartsWith(contextns))
1372                                 return name.Substring(contextns.Length);
1373                         
1374                         return name.Replace("+", ".");
1375                 }
1376
1377                 public string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
1378                 {
1379                         if (quiet)
1380                                 return "";
1381                                 
1382                         ArrayList a = new ArrayList();
1383                         if (arglist != "") a.Add(arglist);
1384                         return MonoImpInfo(assemblyname, typename, membername, a, strlong);
1385                 }
1386
1387                 public string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
1388                 {
1389                         if (quiet)
1390                                 return "";
1391                                 
1392                         ArrayList rgs = new ArrayList ();
1393                         while (itr.MoveNext ())
1394                                 rgs.Add (itr.Current.Value);
1395                         
1396                         return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
1397                 }
1398                 
1399                 public string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong)
1400                 {
1401                         try {
1402                                 Assembly assembly = null;
1403                                 
1404                                 try {
1405                                         assembly = Assembly.LoadWithPartialName(assemblyname);
1406                                 } catch (Exception) {
1407                                         // nothing.
1408                                 }
1409                                 
1410                                 if (assembly == null) {
1411                                         /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
1412                                         else return "";*/
1413                                         return ""; // silently ignore
1414                                 }
1415
1416                                 Type t = assembly.GetType(typename, false);
1417                                 if (t == null) {
1418                                         if (strlong)
1419                                                 return typename + " has not been implemented.";
1420                                         else
1421                                                 return "Not implemented.";
1422                                 }
1423
1424                                 // The following code is flakey and fails to find existing members
1425                                 return "";
1426 #if false
1427                                 MemberInfo[] mis = t.GetMember(membername, BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1428
1429                                 if (mis.Length == 0)
1430                                         return "This member has not been implemented.";
1431                                 if (mis.Length == 1)
1432                                         return MonoImpInfo(mis[0], "member", strlong);
1433
1434                                 // Scan for the appropriate member
1435                                 foreach (MemberInfo mi in mis) {
1436                                         System.Reflection.ParameterInfo[] pis;
1437
1438                                         if (mi is MethodInfo || mi is ConstructorInfo)
1439                                                 pis = ((MethodBase)mi).GetParameters();
1440                                         else if (mi is PropertyInfo)
1441                                                 pis = ((PropertyInfo)mi).GetIndexParameters();
1442                                         else
1443                                                 pis = null;
1444                                         
1445                                         if (pis != null) {
1446                                                 bool good = true;
1447                                                 if (pis.Length != arglist.Count) continue;
1448                                                 for (int i = 0; i < pis.Length; i++) {
1449                                                         if (pis[i].ParameterType.FullName != (string)arglist[i]) { good = false; break; }
1450                                                 }
1451                                                 if (!good) continue;
1452                                         }
1453
1454                                         return MonoImpInfo(mi, "member", strlong);
1455                                 }
1456 #endif
1457                         } catch (Exception) {
1458                                 return "";
1459                         }
1460                 }
1461                 
1462                 public string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
1463                 {
1464                         if (quiet)
1465                                 return "";
1466                                 
1467                         string s = "";
1468
1469                         object[] atts = mi.GetCustomAttributes(true);
1470                         int todoctr = 0;
1471                         foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
1472
1473                         if (todoctr > 0) {
1474                                 if (strlong)
1475                                         s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
1476                                 else 
1477                                         s = "Unfinished.";
1478                         }
1479
1480                         return s;
1481                 }
1482
1483                 public string MonoImpInfo(string assemblyname, string typename, bool strlong)
1484                 {
1485                         if (quiet)
1486                                 return "";
1487                                 
1488                         try {
1489                                 if (assemblyname == "")
1490                                         return "";
1491
1492                                 Assembly assembly = Assembly.LoadWithPartialName(assemblyname);
1493                                 if (assembly == null)
1494                                         return "";
1495
1496                                 Type t = assembly.GetType(typename, false);
1497                                 if (t == null) {
1498                                         if (strlong)
1499                                                 return typename + " has not been implemented.";
1500                                         else
1501                                                 return "Not implemented.";
1502                                 }
1503
1504                                 string s = MonoImpInfo(t, "type", strlong);
1505
1506                                 if (strlong) {
1507                                         MemberInfo[] mis = t.GetMembers(BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1508
1509                                         // Scan members for MonoTODO attributes
1510                                         int mctr = 0;
1511                                         foreach (MemberInfo mi in mis) {
1512                                                 string mii = MonoImpInfo(mi, null, false);
1513                                                 if (mii != "") mctr++; 
1514                                         }
1515                                         if (mctr > 0) {
1516                                                 s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
1517                                         }
1518                                 }
1519
1520                                 return s;
1521
1522                         } catch (Exception) {
1523                                 return "";
1524                         }                       
1525                 }
1526
1527                 public bool MonoEditing ()
1528                 {
1529                         return SettingsHandler.Settings.EnableEditing;
1530                 }
1531                 
1532                 public bool IsToBeAdded(string text) {
1533                         return text.StartsWith("To be added");
1534                 }
1535         }
1536
1537         //
1538         // This takes one of the ecma urls, which look like this:
1539         // ecma:NUMERIC_ID#OPAQUE/REST
1540         //
1541         // NUMERIC_ID is the numeric ID assigned by the compressor
1542         // OPAQUE is opaque for node rendering (it typically contains T:System.Byte for example)
1543         // REST is the rest of the argument used to decode information
1544         //
1545         static string GetFile (string url, out string rest)
1546         {
1547                 int pound = url.IndexOf ("#");
1548                 int slash = url.IndexOf ("/");
1549                 
1550                 string fname = url.Substring (0, pound);
1551                 rest = url.Substring (slash+1);
1552
1553                 return fname;
1554         }
1555
1556 #if false
1557         // This should have a little cache or something.
1558         static XmlDocument GetDocument (HelpSource hs, string fname)
1559         {
1560                 Stream s = hs.GetHelpStream (fname);
1561                 if (s == null){
1562                         Error ("Could not fetch document {0}", fname);
1563                         return null;
1564                 }
1565                 
1566                 XmlDocument doc = new XmlDocument ();
1567
1568                 doc.Load (s);
1569                 
1570                 return doc;
1571         }
1572 #endif
1573         
1574         string GetKindFromCaption (string s)
1575         {
1576                 int p = s.LastIndexOf (' ');
1577                 if (p > 0)
1578                         return s.Substring (p + 1);
1579                 return null;
1580         }
1581         
1582         //
1583         // Obtain an URL of the type T:System.Object from the node
1584         // 
1585         public static string GetNiceUrl (Node node) {
1586                 if (node.Element.StartsWith("N:"))
1587                         return node.Element;
1588                 string name, full;
1589                 int bk_pos = node.Caption.IndexOf (' ');
1590                 // node from an overview
1591                 if (bk_pos != -1) {
1592                         name = node.Caption.Substring (0, bk_pos);
1593                         full = node.Parent.Caption + "." + name.Replace ('.', '+');
1594                         return "T:" + full;
1595                 }
1596                 // node that lists constructors, methods, fields, ...
1597                 if ((node.Caption == "Constructors") || (node.Caption == "Fields") || (node.Caption == "Events") 
1598                         || (node.Caption == "Members") || (node.Caption == "Properties") || (node.Caption == "Methods")
1599                         || (node.Caption == "Operators")) {
1600                         bk_pos = node.Parent.Caption.IndexOf (' ');
1601                         name = node.Parent.Caption.Substring (0, bk_pos);
1602                         full = node.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1603                         return "T:" + full + "/" + node.Element; 
1604                 }
1605                 int pr_pos = node.Caption.IndexOf ('(');
1606                 // node from a constructor
1607                 if (node.Parent.Element == "C") {
1608                         name = node.Parent.Parent.Parent.Caption;
1609                         int idx = node.URL.IndexOf ('/');
1610                         return node.URL[idx+1] + ":" + name + "." + node.Caption.Replace ('.', '+');
1611                 // node from a method with one signature, field, property, operator
1612                 } else if (pr_pos == -1) {
1613                         bk_pos = node.Parent.Parent.Caption.IndexOf (' ');
1614                         name = node.Parent.Parent.Caption.Substring (0, bk_pos);
1615                         full = node.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1616                         int idx = node.URL.IndexOf ('/');
1617                         return node.URL[idx+1] + ":" + full + "." + node.Caption;
1618                 // node from a method with several signatures
1619                 } else {
1620                         bk_pos = node.Parent.Parent.Parent.Caption.IndexOf (' ');
1621                         name = node.Parent.Parent.Parent.Caption.Substring (0, bk_pos);
1622                         full = node.Parent.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1623                         int idx = node.URL.IndexOf ('/');
1624                         return node.URL[idx+1] + ":" + full + "." + node.Caption;
1625                 }
1626         }
1627                                 
1628         //
1629         // Populates the index.
1630         //
1631         public override void PopulateIndex (IndexMaker index_maker)
1632         {
1633                 foreach (Node ns_node in Tree.Nodes){
1634                         foreach (Node type_node in ns_node.Nodes){
1635                                 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1636                                 string full = ns_node.Caption + "." + typename;
1637
1638                                 string doc_tag = GetKindFromCaption (type_node.Caption);
1639                                 string url = "T:" + full;
1640                                         
1641                                 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1642
1643                                         index_maker.Add (type_node.Caption, typename, url);
1644                                         index_maker.Add (full + " " + doc_tag, full, url);
1645
1646                                         foreach (Node c in type_node.Nodes){
1647                                                 switch (c.Caption){
1648                                                 case "Constructors":
1649                                                         index_maker.Add ("  constructors", typename+"0", url + "/C");
1650                                                         break;
1651                                                 case "Fields":
1652                                                         index_maker.Add ("  fields", typename+"1", url + "/F");
1653                                                         break;
1654                                                 case "Events":
1655                                                         index_maker.Add ("  events", typename+"2", url + "/E");
1656                                                         break;
1657                                                 case "Properties":
1658                                                         index_maker.Add ("  properties", typename+"3", url + "/P");
1659                                                         break;
1660                                                 case "Methods":
1661                                                         index_maker.Add ("  methods", typename+"4", url + "/M");
1662                                                         break;
1663                                                 case "Operators":
1664                                                         index_maker.Add ("  operators", typename+"5", url + "/O");
1665                                                         break;
1666                                                 }
1667                                         }
1668
1669                                         //
1670                                         // Now repeat, but use a different sort key, to make sure we come after
1671                                         // the summary data above, start the counter at 6
1672                                         //
1673                                         string keybase = typename + "6.";
1674                                         
1675                                         foreach (Node c in type_node.Nodes){
1676                                                 switch (c.Caption){
1677                                                 case "Constructors":
1678                                                         break;
1679                                                 case "Fields":
1680                                                         foreach (Node nc in c.Nodes){
1681                                                                 string res = nc.Caption;
1682
1683                                                                 string nurl = String.Format ("F:{0}.{1}", full, res);
1684                                                                 index_maker.Add (String.Format ("{0}.{1} field", typename, res),
1685                                                                                  keybase + res, nurl);
1686                                                                 index_maker.Add (String.Format ("{0} field", res), res, nurl);
1687                                                         }
1688
1689                                                         break;
1690                                                 case "Events":
1691                                                         foreach (Node nc in c.Nodes){
1692                                                                 string res = nc.Caption;
1693                                                                 string nurl = String.Format ("E:{0}.{1}", full, res);
1694                                                                 
1695                                                                 index_maker.Add (String.Format ("{0}.{1} event", typename, res),
1696                                                                                  keybase + res, nurl);
1697                                                                 index_maker.Add (String.Format ("{0} event", res), res, nurl);
1698                                                         }
1699                                                         break;
1700                                                 case "Properties":
1701                                                         foreach (Node nc in c.Nodes){
1702                                                                 string res = nc.Caption;
1703                                                                 string nurl = String.Format ("P:{0}.{1}", full, res);
1704                                                                 index_maker.Add (String.Format ("{0}.{1} property", typename, res),
1705                                                                                  keybase + res, nurl);
1706                                                                 index_maker.Add (String.Format ("{0} property", res), res, nurl);
1707                                                         }
1708                                                         break;
1709                                                 case "Methods":
1710                                                         foreach (Node nc in c.Nodes){
1711                                                                 string res = nc.Caption;
1712                                                                 int p = res.IndexOf ("(");
1713                                                                 if (p > 0)
1714                                                                         res = res.Substring (0, p); 
1715                                                                 string nurl = String.Format ("M:{0}.{1}", full, res);
1716                                                                 index_maker.Add (String.Format ("{0}.{1} method", typename, res),
1717                                                                                  keybase + res, nurl);
1718                                                                 index_maker.Add (String.Format ("{0} method", res), res, nurl);
1719                                                         }
1720                                         
1721                                                         break;
1722                                                 case "Operators":
1723                                                         foreach (Node nc in c.Nodes){
1724                                                                 string res = nc.Caption;
1725                                                                 string nurl = String.Format ("O:{0}.{1}", full, res);
1726                                                                 index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
1727                                                                                  keybase + res, nurl);
1728                                                         }
1729                                                         break;
1730                                                 }
1731                                         }
1732                                 } else if (doc_tag == "Enumeration"){
1733                                         //
1734                                         // Enumerations: add the enumeration values
1735                                         //
1736                                         index_maker.Add (type_node.Caption, typename, url);
1737                                         index_maker.Add (full + " " + doc_tag, full, url);
1738
1739                                         // Now, pull the values.
1740                                         string rest;
1741                                         XmlDocument x = GetXmlFromUrl (type_node.URL, out rest);
1742                                         if (x == null)
1743                                                 continue;
1744                                         
1745                                         XmlNodeList members = x.SelectNodes ("/Type/Members/Member");
1746
1747                                         if (members == null)
1748                                                 continue;
1749
1750                                         foreach (XmlNode member_node in members){
1751                                                 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1752                                                 string caption = enum_value + " value";
1753                                                 index_maker.Add (caption, caption, url);
1754                                         }
1755                                 } else if (doc_tag == "Delegate"){
1756                                         index_maker.Add (type_node.Caption, typename, url);
1757                                         index_maker.Add (full + " " + doc_tag, full, url);
1758                                 }
1759                         }
1760                 }
1761         }
1762         //
1763         // Create list of documents for searching
1764         //
1765         public override void PopulateSearchableIndex (IndexWriter writer)
1766         {
1767                 StringBuilder text;
1768                 foreach (Node ns_node in Tree.Nodes) {
1769                         Message (TraceLevel.Info, "\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
1770                         foreach (Node type_node in ns_node.Nodes) {
1771                                 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1772                                 string full = ns_node.Caption + "." + typename;
1773                                 string doc_tag = GetKindFromCaption (type_node.Caption);
1774                                 string url = "T:" + full;
1775                                 string rest;
1776                                 XmlDocument xdoc = GetXmlFromUrl (type_node.URL, out rest);
1777                                 if (xdoc == null)
1778                                         continue;
1779                                 
1780                                 // 
1781                                 // For classes, structures or interfaces add a doc for the overview and
1782                                 // add a doc for every constructor, method, event, ...
1783                                 // 
1784                                 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1785                                         
1786                                         // Adds a doc for every overview of every type
1787                                         SearchableDocument doc = new SearchableDocument ();
1788                                         doc.title = type_node.Caption;
1789                                         doc.hottext = typename;
1790                                         doc.url = url;
1791                                         
1792                                         XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1793                                         text  = new StringBuilder ();
1794                                         GetTextFromNode (node_sel, text);
1795                                         doc.text = text.ToString ();
1796
1797                                         text  = new StringBuilder ();
1798                                         GetExamples (node_sel, text);
1799                                         doc.examples = text.ToString ();
1800                                         
1801                                         writer.AddDocument (doc.LuceneDoc);
1802
1803                                         //Add docs for contructors, methods, etc.
1804                                         foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
1805                                                 
1806                                                 if (c.Element == "*")
1807                                                         continue;
1808                                                 int i = 1;
1809                                                 foreach (Node nc in c.Nodes) {
1810                                                         //xpath to the docs xml node
1811                                                         string xpath;
1812                                                         if (c.Caption == "Constructors")
1813                                                                 xpath = String.Format ("/Type/Members/Member[{0}]/Docs", i++);
1814                                                         else if (c.Caption == "Operators")
1815                                                                 xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption);
1816                                                         else
1817                                                                 xpath = String.Format ("/Type/Members/Member[@MemberName='{0}']/Docs", nc.Caption);
1818                                                         //construct url of the form M:Array.Sort
1819                                                         string urlnc;
1820                                                         if (c.Caption == "Constructors")
1821                                                                 urlnc = String.Format ("{0}:{1}.{2}", c.Caption[0], ns_node.Caption, nc.Caption);
1822                                                         else
1823                                                                 urlnc = String.Format ("{0}:{1}.{2}.{3}", c.Caption[0], ns_node.Caption, typename, nc.Caption);
1824
1825                                                         //create the doc
1826                                                         SearchableDocument doc_nod = new SearchableDocument ();
1827                                                         doc_nod.title = LargeName (nc);
1828                                                         //dont add the parameters to the hottext
1829                                                         int ppos = nc.Caption.IndexOf ('(');
1830                                                         if (ppos != -1)
1831                                                                 doc_nod.hottext =  nc.Caption.Substring (0, ppos);
1832                                                         else
1833                                                                 doc_nod.hottext = nc.Caption;
1834
1835                                                         doc_nod.url = urlnc;
1836
1837                                                         XmlNode xmln = xdoc.SelectSingleNode (xpath);
1838                                                         if (xmln == null) {
1839                                                                 Error ("Problem: {0}, with xpath: {1}", urlnc, xpath);
1840                                                                 continue;
1841                                                         }
1842
1843                                                         text = new StringBuilder ();
1844                                                         GetTextFromNode (xmln, text);
1845                                                         doc_nod.text = text.ToString ();
1846
1847                                                         text = new StringBuilder ();
1848                                                         GetExamples (xmln, text);
1849                                                         doc_nod.examples = text.ToString ();
1850
1851                                                         writer.AddDocument (doc_nod.LuceneDoc);
1852                                                 }
1853                                         }
1854                                 //
1855                                 // Enumerations: add the enumeration values
1856                                 //
1857                                 } else if (doc_tag == "Enumeration"){
1858                                                                                 
1859                                         XmlNodeList members = xdoc.SelectNodes ("/Type/Members/Member");
1860                                         if (members == null)
1861                                                 continue;
1862
1863                                         text = new StringBuilder ();
1864                                         foreach (XmlNode member_node in members) {
1865                                                 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1866                                                 text.Append (enum_value);
1867                                                 text.Append (" ");
1868                                                 GetTextFromNode (member_node["Docs"], text);
1869                                                 text.Append ("\n");
1870                                         }
1871                                         SearchableDocument doc = new SearchableDocument ();
1872
1873                                         text = new StringBuilder ();
1874                                         GetExamples (xdoc.SelectSingleNode ("/Type/Docs"), text);
1875                                         doc.examples = text.ToString ();
1876
1877                                         doc.title = type_node.Caption;
1878                                         doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1879                                         doc.url = url;
1880                                         doc.text = text.ToString();
1881                                         writer.AddDocument (doc.LuceneDoc);
1882                                 //
1883                                 // Add delegates
1884                                 //
1885                                 } else if (doc_tag == "Delegate"){
1886                                         SearchableDocument doc = new SearchableDocument ();
1887                                         doc.title = type_node.Caption;
1888                                         doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1889                                         doc.url = url; 
1890                                         
1891                                         XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1892
1893                                         text = new StringBuilder ();
1894                                         GetTextFromNode (node_sel, text);
1895                                         doc.text = text.ToString();
1896
1897                                         text = new StringBuilder ();
1898                                         GetExamples (node_sel, text);
1899                                         doc.examples = text.ToString();
1900
1901                                         writer.AddDocument (doc.LuceneDoc);
1902                                 } 
1903                         }
1904                 }
1905         }
1906         
1907         //
1908         // Extract the interesting text from the docs node
1909         //
1910         void GetTextFromNode (XmlNode n, StringBuilder sb) 
1911         {
1912                 //don't include example code
1913                 if (n.Name == "code") 
1914                         return;
1915
1916                 //include the url to which points the see tag
1917                 if (n.Name == "see" && n.Attributes.Count > 0)
1918                                 sb.Append (n.Attributes [0].Value);
1919                 
1920                 //include the name of the parameter
1921                 if (n.Name == "paramref" && n.Attributes.Count > 0)
1922                         sb.Append (n.Attributes [0].Value);
1923
1924                 //include the contents for the node that contains text
1925                 if (n.NodeType == XmlNodeType.Text)
1926                         sb.Append (n.Value);
1927                 
1928                 //add the rest of xml tags recursively
1929                 if (n.HasChildNodes)
1930                         foreach (XmlNode n_child in n.ChildNodes)
1931                                 GetTextFromNode (n_child, sb);
1932         }
1933         //
1934         // Extract the code nodes from the docs
1935         //
1936         void GetExamples (XmlNode n, StringBuilder sb)
1937         {
1938                 if (n.Name == "code") {
1939                         sb.Append (n.InnerText);
1940                 } else {
1941                         if (n.HasChildNodes)
1942                                 foreach (XmlNode n_child in n.ChildNodes)
1943                                         GetExamples (n_child, sb);
1944                 }
1945         }
1946         //
1947         // Extract a large name for the Node
1948         //  (copied from mono-tools/docbrowser/browser.Render()
1949         static string LargeName (Node matched_node)
1950         {
1951                 string[] parts = matched_node.URL.Split('/', '#');                      
1952                 if(parts.Length == 3 && parts[2] != String.Empty) { //List of Members, properties, events, ...
1953                         return parts[1] + ": " + matched_node.Caption;
1954                 } else if(parts.Length >= 4) { //Showing a concrete Member, property, ...                                       
1955                         return parts[1] + "." + matched_node.Caption;
1956                 } else {
1957                         return matched_node.Caption;
1958                 }
1959         }
1960
1961 }
1962
1963 public class EcmaUncompiledHelpSource : EcmaHelpSource {
1964         readonly DirectoryInfo basedir;
1965         readonly XmlDocument basedoc;
1966         
1967         public new readonly string Name;
1968         public     readonly string BasePath;
1969         
1970         public EcmaUncompiledHelpSource (string base_file) : base ()
1971         {
1972                 Message (TraceLevel.Info, "Loading uncompiled help from " + base_file);
1973                 
1974                 basedir = new DirectoryInfo(base_file);
1975                 BasePath = basedir.FullName;
1976                 
1977                 basedoc = new XmlDocument();
1978                 basedoc.Load(Path.Combine(basedir.FullName, "index.xml"));
1979                 
1980                 Name = basedoc.SelectSingleNode("Overview/Title").InnerText;
1981                 
1982                 bool has_content = false;
1983                 
1984                 foreach (XmlElement ns in basedoc.SelectNodes("Overview/Types/Namespace")) {
1985                         has_content = true;
1986                         Node nsnode = Tree.CreateNode(ns.GetAttribute("Name"), "N:" + ns.GetAttribute("Name"));
1987                         
1988                         bool has_types = false;
1989                         foreach (XmlElement t in ns.SelectNodes("Type")) {
1990                                 has_types = true;
1991                                 string typename = EcmaDoc.GetDisplayName (t).Replace("+", ".");
1992                                 
1993                                 // Must load in each document to get the list of members...
1994                                 XmlDocument typedoc = new XmlDocument();
1995                                 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns.GetAttribute("Name")), t.GetAttribute("Name") + ".xml"));
1996                                 string kind = EcmaDoc.GetTypeKind (typedoc);
1997                                 
1998                                 string url = ns.GetAttribute("Name") + "." + t.GetAttribute("Name");
1999                                 Node typenode = nsnode.CreateNode(typename + " " + kind, "T:" + url);                           
2000                                 //Node typemembers = typenode.CreateNode("Members", "T:" + url + "/*");
2001                                 
2002                                 Hashtable groups = new Hashtable();
2003                                 Hashtable groups_count = new Hashtable();
2004                                 foreach (XmlElement member in typedoc.SelectNodes("Type/Members/Member")) {
2005                                         string membername = member.GetAttribute("MemberName");
2006                                         string membertype = member.SelectSingleNode("MemberType").InnerText;
2007                                         
2008                                         if (membertype == "Constructor")
2009                                                 membername = t.GetAttribute("Name");
2010                                         if (membername.StartsWith("op_"))
2011                                                 membertype = "Operator";
2012                                         
2013                                         Node group;
2014                                         if (groups.ContainsKey(membertype)) {
2015                                                 group = (Node)groups[membertype];
2016                                         } else {
2017                                                 string membertypeplural = membertype + "s";
2018                                                 if (membertypeplural == "Propertys") membertypeplural = "Properties";
2019                                                 
2020                                                 group = typenode.CreateNode(membertypeplural, "T:" + url + "/" + membertype[0]);
2021                                                 groups[membertype] = group;
2022                                                 groups_count[membertype] = 0;
2023                                         }
2024                                         
2025                                         if (membertype == "Constructor" || membertype == "Method" || 
2026                                                 (membertype == "Property" && member.SelectNodes("Parameters/Parameter").Count > 0)) {
2027                                                 membername = EcmaHelpSource.MakeSignature(member, membertype == "Constructor" ? membername : null);
2028                                         } else if (membertype == "Operator") {
2029                                                 string dummy;
2030                                                 EcmaHelpSource.MakeOperatorSignature(member, out dummy, out membername);
2031                                         }
2032                                         
2033                                         int index = (int)groups_count[membertype];
2034                                         groups_count[membertype] = index + 1;
2035                                         
2036                                         group.CreateNode(membername, index.ToString());
2037                                 }
2038
2039                                 foreach (Node group in groups.Values)
2040                                         group.Sort();                   
2041                         }
2042                         
2043                         if (has_types)
2044                                 nsnode.Sort();
2045                 }
2046                 
2047                 if (has_content)
2048                         Tree.Sort();
2049         }
2050         
2051         public override string GetIdFromUrl (string prefix, string ns, string type)
2052         {
2053                 if (prefix != "T:")
2054                         throw new NotImplementedException();
2055                 return Path.Combine(Path.Combine(basedir.FullName, ns), type + ".xml");
2056         }
2057
2058         protected override XmlDocument GetXmlFromUrl(string url, out string rest) {
2059                 // strip off the T:
2060                 url = url.Substring(2);
2061                 
2062                 int sidx = url.IndexOf("/");
2063                 if (sidx == -1) {
2064                         rest = "";
2065                 } else {
2066                         rest = url.Substring(sidx+1);
2067                         url = url.Substring(0, sidx);
2068                 }
2069                 
2070                 string ns, type;
2071                 if (!RootTree.GetNamespaceAndType (url, out ns, out type)) {
2072                         Message (TraceLevel.Error, "Could not determine namespace/type for {0}", url);
2073                         return null;
2074                 }
2075                 
2076                 string file = Path.Combine(Path.Combine(basedir.FullName, ns), 
2077                                 ToEscapedTypeName (type).Replace ('.', '+') + ".xml");
2078                 if (!new FileInfo(file).Exists) return null;
2079                 
2080                 XmlDocument typedoc = new XmlDocument();
2081                 typedoc.Load(file);
2082                 return typedoc;
2083         }
2084         
2085         public override string GetText (string url, out Node match_node) {
2086                 if (url == "root:") {
2087                         match_node = null;
2088                         
2089                         //load index.xml
2090                         XmlDocument index = new XmlDocument ();
2091                         index.Load (Path.Combine (basedir.FullName, "index.xml"));
2092                         XmlNodeList nodes = index.SelectNodes ("/Overview/Types/Namespace");
2093                         
2094                         //recreate masteroverview.xml
2095                         XmlDocument summary = new XmlDocument ();
2096                         XmlElement elements = summary.CreateElement ("elements");
2097                         foreach (XmlNode node in nodes) {
2098                                 XmlElement ns = summary.CreateElement ("namespace");
2099                                 XmlAttribute attr = summary.CreateAttribute ("ns");
2100                                 attr.Value = EcmaDoc.GetDisplayName (node);
2101                                 ns.Attributes.Append (attr);
2102                                 elements.AppendChild (ns);
2103                         }
2104                         summary.AppendChild (elements);
2105
2106                         XmlReader reader = new XmlTextReader (new StringReader (summary.OuterXml));
2107
2108                         //transform the recently created masteroverview.xml
2109                         XsltArgumentList args = new XsltArgumentList();
2110                         args.AddExtensionObject("monodoc:///extensions", ExtObject);
2111                         args.AddParam("show", "", "masteroverview");
2112                         string s = Htmlize(new XPathDocument (reader), args);
2113                         return BuildHtml (css_ecma_code, js_code, s); 
2114                 }
2115                 return base.GetText(url, out match_node);
2116         }
2117         
2118         protected override XmlDocument GetNamespaceDocument (string ns)
2119         {
2120                 XmlDocument nsdoc = new XmlDocument();
2121                 nsdoc.Load (EcmaDoc.GetNamespaceFile (basedir.FullName, ns));
2122                 
2123                 XmlDocument elements = new XmlDocument();
2124                 XmlElement docnode = elements.CreateElement("elements");
2125                 elements.AppendChild (docnode);
2126                 
2127                 foreach (XmlElement doc in nsdoc.SelectNodes("Namespace/Docs/*")) {
2128                         docnode.AppendChild(elements.ImportNode(doc, true));
2129                 }
2130                                 
2131                 foreach (XmlElement t in basedoc.SelectNodes("Overview/Types/Namespace[@Name='" + ns + "']/Type")) {
2132                         XmlDocument typedoc = new XmlDocument();
2133                         typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns), t.GetAttribute("Name") + ".xml"));
2134                         
2135                         string typekind;
2136                         switch (EcmaDoc.GetTypeKind(typedoc)) {
2137                         case "Class": typekind = "class"; break;
2138                         case "Enumeration": typekind = "enum"; break;
2139                         case "Structure": typekind = "struct"; break;
2140                         case "Delegate": typekind = "delegate"; break;
2141                         case "Interface": typekind = "interface"; break;
2142                         default: throw new InvalidOperationException();
2143                         }
2144                         
2145                         XmlElement typenode = elements.CreateElement(typekind);
2146                         typenode.SetAttribute("name", EcmaDoc.GetDisplayName (t).Replace ('+', '.'));
2147                         typenode.SetAttribute("fullname", ns + "." + t.GetAttribute("Name"));
2148                         typenode.AppendChild(elements.ImportNode(typedoc.SelectSingleNode("Type/Docs/summary"), true));
2149                         
2150                         docnode.AppendChild(typenode);
2151                 }
2152
2153                 return elements;
2154         }
2155         
2156         public override Stream GetHelpStream (string id)
2157         {
2158                 if (id == "ExtensionMethods.xml") {
2159                         // TODO: generate ExtensionMethods.xml based on index.xml contents.
2160                 }
2161                 return null;
2162         }
2163
2164         public override XmlDocument GetHelpXmlWithChanges (string id)
2165         {
2166                 XmlDocument doc = new XmlDocument ();
2167                 doc.Load (id);
2168                 return doc;
2169         }
2170         
2171         class UncompiledResolver : XmlResolver {
2172                 public override Uri ResolveUri (Uri baseUri, string relativeUri)
2173                 {
2174                         return null;
2175                 }
2176
2177                 public override object GetEntity (Uri absoluteUri, string role, Type ofObjectToReturn)
2178                 {
2179                         return null;
2180                 }
2181
2182                 public override System.Net.ICredentials Credentials {
2183                         set {/* ignore */}
2184                 }
2185         }
2186
2187         protected override XmlResolver CreateDocumentResolver ()
2188         {
2189                 return new UncompiledResolver ();
2190         }
2191 }
2192
2193 }
2194
2195