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