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