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