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