* Mono.Documentation/monodocer.cs: Only provide a Main() method and
[mono.git] / mcs / tools / mdoc / Mono.Documentation / monodocer.cs
1 // Updater program for syncing Mono's ECMA-style documentation files
2 // with an assembly.
3 // By Joshua Tauberer <tauberer@for.net>
4
5 using System;
6 using System.Collections;
7 #if !NET_1_0
8 using System.Collections.Generic;
9 #endif
10 using System.Globalization;
11 using System.IO;
12 using System.Text;
13 using System.Reflection;
14 using System.Xml;
15 using System.Xml.XPath;
16
17 #if NET_1_0
18 using Mono.GetOptions;
19
20 using MemberInfoEnumerable = System.Collections.IEnumerable;
21 using MyXmlNodeList        = System.Collections.ArrayList;
22 using StringList           = System.Collections.ArrayList;
23 using StringToStringMap    = System.Collections.Hashtable;
24 using StringToXmlNodeMap   = System.Collections.Hashtable;
25 #else
26 using MemberInfoEnumerable = System.Collections.Generic.IEnumerable<System.Reflection.MemberInfo>;
27 using MyXmlNodeList        = System.Collections.Generic.List<System.Xml.XmlNode>;
28 using StringList           = System.Collections.Generic.List<string>;
29 using StringToStringMap    = System.Collections.Generic.Dictionary<string, string>;
30 using StringToXmlNodeMap   = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
31 #endif
32
33 namespace Mono.Documentation {
34
35 class MDocUpdaterOptions 
36 #if NET_1_0
37         : Options
38 #endif
39 {
40 #if NET_1_0
41         [Option("The root {directory} of an assembly's documentation files.")]
42 #endif
43         public string path = null;
44
45 #if NET_1_0
46         [Option("When updating documentation, write the updated files to this {path}.")]
47 #endif
48         public string updateto = null;
49
50 #if NET_1_0
51         [Option(-1, "The assembly to document.  Specify a {file} path or the name of a GAC'd assembly.")]
52 #endif
53         public string[] assembly = null;
54
55 #if NET_1_0
56         [Option(-1, "Document only the {type name}d by this argument.")]
57 #endif
58         public string[] type = null;
59
60 #if NET_1_0
61         [Option("Update only the types in this {namespace}.")]
62 #endif
63         public string @namespace = null;
64
65 #if NET_1_0
66         [Option("Allow monodocer to delete members from files.")]
67 #endif
68         public bool delete = false;
69
70 #if NET_1_0
71         [Option("Include overridden methods in documentation.")]
72 #endif
73         public bool overrides = false;
74
75 #if NET_1_0
76         [Option("Don't update members.")]
77 #endif
78         public bool ignoremembers = false;
79
80 #if NET_1_0
81         [Option("Don't rename documentation XML files for missing types.  IGNORED.")]
82 #endif
83         public bool ignore_extra_docs = false;
84
85 #if NET_1_0
86         [Option("The {name} of the project this documentation is for.")]
87 #endif
88         public string name;
89
90 #if NET_1_0
91         [Option("An XML documentation {file} made by the /doc option of mcs/csc the contents of which will be imported.")]
92 #endif
93         public string importslashdoc;
94         
95 #if NET_1_0
96         [Option("An ECMA or monodoc-generated XML documemntation {file} to import.")]
97 #endif
98         public string importecmadoc;
99
100 #if NET_1_0
101         [Option("Import either a /doc or ECMA documentation file.")]
102 #endif
103         public string import;
104
105 #if NET_1_0
106         [Option("Indent the XML files nicely.")]
107 #endif
108         public bool pretty = false;
109         
110 #if NET_1_0
111         [Option("Create a <since/> element for added types/members with the value {since}.")]
112 #endif
113         public string since;
114
115 #if NET_1_0
116         [Option("Show full stack trace on error.")]
117 #endif
118         public bool show_exceptions;
119 }
120
121 class MDocUpdater {
122         
123         static string srcPath;
124         static Assembly[] assemblies;
125         
126         static bool nooverrides = true, delete = false, ignoremembers = false;
127         static bool pretty = false;
128         static bool show_exceptions = false;
129         
130         static int additions = 0, deletions = 0;
131
132         static string name;
133         static XmlDocument slashdocs;
134         static XmlReader ecmadocs;
135
136         static string since;
137
138         static MemberFormatter csharpFullFormatter  = new CSharpFullMemberFormatter ();
139         static MemberFormatter csharpFormatter      = new CSharpMemberFormatter ();
140         static MemberFormatter docTypeFormatter     = new DocTypeMemberFormatter ();
141         static MemberFormatter slashdocFormatter    = new SlashDocMemberFormatter ();
142         static MemberFormatter filenameFormatter    = new FileNameMemberFormatter ();
143
144         static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
145
146         const BindingFlags DefaultBindingFlags = 
147                 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
148         
149 #if NET_1_0
150         public static void Main(string[] args)
151         {
152                 MDocUpdaterOptions opts = new MDocUpdaterOptions ();
153                 opts.ProcessArgs(args);
154
155                 if (args.Length == 0) {
156                         opts.DoHelp();
157                         return;
158                 }
159                 Run (opts);
160         }
161 #endif
162                 
163         public static void Run (MDocUpdaterOptions opts)
164         {
165                 nooverrides = !opts.overrides;
166                 delete = opts.delete;
167                 ignoremembers = opts.ignoremembers;
168                 name = opts.name;
169                 pretty = opts.pretty;
170                 since = opts.since;
171                 show_exceptions = opts.show_exceptions;
172
173                 try {
174                         // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
175                         
176                         if (opts.path == null)
177                                 throw new InvalidOperationException("The path option is required.");
178                         
179                         srcPath = opts.path;
180
181                         if (opts.type != null && opts.type.Length > 0 && opts.@namespace != null)
182                                 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
183                         
184                         if (opts.assembly == null)
185                                 throw new InvalidOperationException("The assembly option is required.");
186                                 
187                         assemblies = new Assembly [opts.assembly.Length];
188                         for (int i = 0; i < opts.assembly.Length; i++)
189                                 assemblies [i] = LoadAssembly (opts.assembly [i]);
190                                 
191                         // IMPORT FROM /DOC?
192                         
193                         if (opts.importslashdoc != null) {
194                                 try {
195                                         slashdocs = new XmlDocument();
196                                         slashdocs.Load(opts.importslashdoc);
197                                 } catch (Exception e) {
198                                         Error ("Could not load /doc file: {0}", e.Message);
199                                         Environment.ExitCode = 1;
200                                         return;
201                                 }
202                         }
203                         
204                         if (opts.importecmadoc != null) {
205                                 try {
206                                         ecmadocs = new XmlTextReader (opts.importecmadoc);
207                                 } catch (Exception e) {
208                                         Error ("Could not load ECMA XML file: {0}", e.Message);
209                                         Environment.ExitCode = 1;
210                                         return;
211                                 }
212                         }
213
214                         if (opts.import != null && ecmadocs == null && slashdocs == null) {
215                                 try {
216                                         XmlReader r = new XmlTextReader (opts.import);
217                                         if (r.Read ()) {
218                                                 while (r.NodeType != XmlNodeType.Element) {
219                                                         if (!r.Read ())
220                                                                 throw new Exception ("Unable to read XML file: " + 
221                                                                                 opts.import);
222                                                 }
223                                                 if (r.LocalName == "doc") {
224                                                         slashdocs = new XmlDocument();
225                                                         slashdocs.Load (opts.import);
226                                                 }
227                                                 else if (r.LocalName == "Libraries") {
228                                                         ecmadocs = new XmlTextReader (opts.import);
229                                                 }
230                                                 else
231                                                         throw new Exception ("Unsupported XML format within " + opts.import);
232                                         }
233                                         r.Close ();
234                                 } catch (Exception e) {
235                                         Error ("Could not load XML file: {0}", e.Message);
236                                         Environment.ExitCode = 1;
237                                         return;
238                                 }
239                         }
240                         
241                         // PERFORM THE UPDATES
242                         
243                         string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
244                         if (opts.type != null && opts.type.Length > 0)
245                                 DoUpdateTypes(opts.path, opts.type, dest_dir);
246                         else if (opts.@namespace != null)
247                                 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
248                                                 Path.Combine (dest_dir, opts.@namespace));
249                         else
250                                 DoUpdateAssemblies(opts.path, dest_dir);
251                 
252                 } catch (InvalidOperationException error) {
253                         Error (opts.show_exceptions ? error.ToString () : error.Message);
254                         Environment.ExitCode = 1;
255                         return;
256                         
257                 } catch (System.IO.IOException error) {
258                         Error (opts.show_exceptions ? error.ToString () : error.Message);
259                         Environment.ExitCode = 1;
260                         return;
261
262                 } catch (Exception error) {
263                         Error (opts.show_exceptions ? error.ToString () : error.Message);
264                         Environment.ExitCode = 1;
265                 }
266
267                 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
268         }
269
270         private static void Error (object message)
271         {
272                 Console.Error.Write ("monodocer: ");
273                 Console.Error.WriteLine (message);
274         }
275         
276         private static void Error (string format, params object[] args)
277         {
278                 Console.Error.Write ("monodocer: ");
279                 Console.Error.WriteLine (format, args);
280         }
281         
282         private static Assembly LoadAssembly (string name)
283         {
284                 Assembly assembly = null;
285                 try {
286                         assembly = Assembly.LoadFile (name);
287                 } catch (System.IO.FileNotFoundException) { }
288
289                 if (assembly == null) {
290                         try {
291                                 assembly = Assembly.LoadWithPartialName (name);
292                         } catch (Exception) { }
293                 }
294                         
295                 if (assembly == null)
296                         throw new InvalidOperationException("Assembly " + name + " not found.");
297
298                 return assembly;
299         }
300
301         private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
302                 OrderTypeAttributes (element);
303                 XmlTextWriter writer = new XmlTextWriter(output);
304                 writer.Formatting = Formatting.Indented;
305                 writer.Indentation = 2;
306                 writer.IndentChar = ' ';
307                 element.WriteTo(writer);
308                 output.WriteLine();     
309         }
310
311         private static void OrderTypeAttributes (XmlElement e)
312         {
313                 foreach (XmlElement type in e.SelectNodes ("//Type")) {
314                         OrderTypeAttributes (type.Attributes);
315                 }
316         }
317
318         static readonly string[] TypeAttributeOrder = {
319                 "Name", "FullName", "FullNameSP", "Maintainer"
320         };
321
322         private static void OrderTypeAttributes (XmlAttributeCollection c)
323         {
324                 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
325                 for (int i = 0; i < c.Count; ++i) {
326                         XmlAttribute a = c [i];
327                         for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
328                                 if (a.Name == TypeAttributeOrder [j]) {
329                                         attrs [j] = a;
330                                         break;
331                                 }
332                         }
333                 }
334                 for (int i = attrs.Length-1; i >= 0; --i) {
335                         XmlAttribute n = attrs [i];
336                         if (n == null)
337                                 continue;
338                         XmlAttribute r = null;
339                         for (int j = i+1; j < attrs.Length; ++j) {
340                                 if (attrs [j] != null) {
341                                         r = attrs [j];
342                                         break;
343                                 }
344                         }
345                         if (r == null)
346                                 continue;
347                         c.Remove (n);
348                         c.InsertBefore (n, r);
349                 }
350         }
351         
352         private static XmlDocument CreateIndexStub() {
353                 XmlDocument index = new XmlDocument();
354
355                 XmlElement index_root = index.CreateElement("Overview");
356                 index.AppendChild(index_root);
357
358                 if (assemblies.Length == 0)
359                         throw new Exception ("No assembly");
360
361                 XmlElement index_assemblies = index.CreateElement("Assemblies");
362                 index_root.AppendChild(index_assemblies);
363
364                 XmlElement index_remarks = index.CreateElement("Remarks");
365                 index_remarks.InnerText = "To be added.";
366                 index_root.AppendChild(index_remarks);
367
368                 XmlElement index_copyright = index.CreateElement("Copyright");
369                 index_copyright.InnerText = "To be added.";
370                 index_root.AppendChild(index_copyright);
371
372                 XmlElement index_types = index.CreateElement("Types");
373                 index_root.AppendChild(index_types);
374                 
375                 return index;
376         }
377         
378         private static void WriteNamespaceStub(string ns, string outdir) {
379                 XmlDocument index = new XmlDocument();
380
381                 XmlElement index_root = index.CreateElement("Namespace");
382                 index.AppendChild(index_root);
383                 
384                 index_root.SetAttribute("Name", ns);
385
386                 XmlElement index_docs = index.CreateElement("Docs");
387                 index_root.AppendChild(index_docs);
388
389                 XmlElement index_summary = index.CreateElement("summary");
390                 index_summary.InnerText = "To be added.";
391                 index_docs.AppendChild(index_summary);
392
393                 XmlElement index_remarks = index.CreateElement("remarks");
394                 index_remarks.InnerText = "To be added.";
395                 index_docs.AppendChild(index_remarks);
396
397                 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml",  FileMode.CreateNew)) {
398                         WriteXml(index.DocumentElement, writer);
399                 }
400         }
401
402         public static void DoUpdateTypes(string basepath, string[] typenames, string dest) {
403                 ArrayList found = new ArrayList ();
404                 foreach (Assembly assembly in assemblies) {
405                         foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
406                                 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
407                                 if (relpath != null)
408                                         found.Add (docsTypeInfo.Type.FullName);
409                         }
410                 }
411                 StringList notFound = new StringList (typenames.Length);
412                 foreach (string typename in typenames)
413                         if (!found.Contains (typename))
414                                 notFound.Add (typename);
415                 if (notFound.Count > 0)
416                         throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", DocUtils.ToStringArray (notFound)));
417         }
418
419         public static string DoUpdateType(Type type, string basepath, string dest, XmlReader ecmaDocsType)
420         {
421                 if (type.Namespace == null)
422                         Error ("warning: The type `{0}' is in the root namespace.  This may cause problems with display within monodoc.",
423                                         type.FullName);
424                 if (!IsPublic (type))
425                         return null;
426                 
427                 // Must get the A+B form of the type name.
428                 string typename = GetTypeFileName(type);
429                 
430                 string reltypefile = DocUtils.PathCombine (type.Namespace, typename + ".xml");
431                 string typefile = Path.Combine (basepath, reltypefile);
432                 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
433
434                 string output = null;
435                 if (dest == null) {
436                         output = typefile;
437                 } else if (dest == "-") {
438                         output = null;
439                 } else {
440                         output = Path.Combine (dest, reltypefile);
441                 }       
442
443                 if (file.Exists) {
444                         // Update
445                         XmlDocument basefile = new XmlDocument();
446                         if (!pretty) basefile.PreserveWhitespace = true;
447                         try {
448                                 basefile.Load(typefile);
449                         } catch (Exception e) {
450                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
451                         }
452                         
453                         DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
454                 } else {
455                         // Stub
456                         XmlElement td = StubType(type, output, ecmaDocsType);
457                         if (td == null)
458                                 return null;
459                         
460                         System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
461                         if (!dir.Exists) {
462                                 dir.Create();
463                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
464                         }
465                 }
466                 return reltypefile;
467         }
468
469         private static XPathNavigator SelectSingleNode (XPathNavigator n, string xpath)
470         {
471 #if !NET_1_0
472                 return n.SelectSingleNode (xpath);
473 #else
474                 XPathNodeIterator i = n.Select (xpath);
475                 XPathNavigator r = null;
476                 while (i.MoveNext ()) {
477                         r = i.Current;
478                 }
479                 return r;
480 #endif
481         }
482
483         public static void DoUpdateNS(string ns, string nspath, string outpath) {
484                 Hashtable seenTypes = new Hashtable();
485                 Assembly assembly = assemblies [0];
486                 
487                 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
488                         XmlDocument basefile = new XmlDocument();
489                         if (!pretty) basefile.PreserveWhitespace = true;
490                         string typefile = Path.Combine(nspath, file.Name);
491                         try {
492                                 basefile.Load(typefile);
493                         } catch (Exception e) {
494                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
495                         }
496
497                         string typename = 
498                                 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
499                         Type type = assembly.GetType(typename, false);
500                         if (type == null) {
501                                 Error ("Type no longer in assembly: " + typename);
502                                 continue;
503                         }                       
504
505                         seenTypes[type] = seenTypes;
506                         DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
507                 }
508                 
509                 // Stub types not in the directory
510                 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
511                         Type type = docsTypeInfo.Type;
512                         if (type.Namespace != ns || seenTypes.ContainsKey(type))
513                                 continue;
514
515                         XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
516                         if (td == null) continue;
517                 }
518         }
519         
520         private static string GetTypeFileName(Type type) {
521                 return filenameFormatter.GetName (type);
522         }
523
524         public static string GetTypeFileName (string typename)
525         {
526                 StringBuilder filename = new StringBuilder (typename.Length);
527                 int numArgs = 0;
528                 int numLt = 0;
529                 bool copy = true;
530                 for (int i = 0; i < typename.Length; ++i) {
531                         char c = typename [i];
532                         switch (c) {
533                                 case '<':
534                                         copy = false;
535                                         ++numLt;
536                                         break;
537                                 case '>':
538                                         --numLt;
539                                         if (numLt == 0) {
540                                                 filename.Append ('`').Append ((numArgs+1).ToString());
541                                                 numArgs = 0;
542                                                 copy = true;
543                                         }
544                                         break;
545                                 case ',':
546                                         if (numLt == 1)
547                                                 ++numArgs;
548                                         break;
549                                 default:
550                                         if (copy)
551                                                 filename.Append (c);
552                                         break;
553                         }
554                 }
555                 return filename.ToString ();
556         }
557
558         
559         private static void AddIndexAssembly (Assembly assembly, XmlElement parent)
560         {
561                 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
562                 index_assembly.SetAttribute("Name", assembly.GetName().Name);
563                 index_assembly.SetAttribute("Version", assembly.GetName().Version.ToString());
564                 MakeAttributes(index_assembly, assembly, true);
565                 parent.AppendChild(index_assembly);
566         }
567
568         private static void DoUpdateAssemblies (string source, string dest) 
569         {
570                 string indexfile = dest + "/index.xml";
571                 XmlDocument index;
572                 if (System.IO.File.Exists(indexfile)) {
573                         index = new XmlDocument();
574                         index.Load(indexfile);
575
576                         // Format change
577                         ClearElement(index.DocumentElement, "Assembly");
578                         ClearElement(index.DocumentElement, "Attributes");
579                 } else {
580                         index = CreateIndexStub();
581                 }
582                 
583                 if (name == null) {
584                         string defaultTitle = "Untitled";
585                         if (assemblies.Length == 1)
586                                 defaultTitle = assemblies[0].GetName().Name;
587                         WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
588                 } else {
589                         WriteElementText(index.DocumentElement, "Title", name);
590                 }
591                 
592                 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
593                 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
594                 index_assemblies.RemoveAll ();
595                 
596                 Hashtable goodfiles = new Hashtable();
597
598                 foreach (Assembly assm in assemblies) {
599                         AddIndexAssembly (assm, index_assemblies);
600                         DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
601                 }
602
603                 SortIndexEntries (index_types);
604                 
605                 CleanupFiles (dest, goodfiles);
606                 CleanupIndexTypes (index_types, goodfiles);
607                 CleanupExtensions (index_types);
608
609                 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
610                         WriteXml(index.DocumentElement, writer);
611         }
612                 
613         private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
614
615         private static void DoUpdateAssembly (Assembly assembly, XmlElement index_types, string source, string dest, Hashtable goodfiles) 
616         {
617                 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
618                         Type type = docTypeInfo.Type;
619                         if (!IsPublic (type) || type.FullName.IndexOfAny (InvalidFilenameChars) >= 0)
620                                 continue;
621
622                         string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
623                         if (reltypepath == null)
624                                 continue;
625                         
626                         // Add namespace and type nodes into the index file as needed
627                         XmlElement nsnode = (XmlElement)index_types.SelectSingleNode("Namespace[@Name='" + type.Namespace + "']");
628                         if (nsnode == null) {
629                                 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
630                                 nsnode.SetAttribute("Name", type.Namespace);
631                                 index_types.AppendChild(nsnode);
632                         }
633                         string typename = GetTypeFileName(type);
634                         string doc_typename = GetDocTypeName (type);
635                         XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
636                         if (typenode == null) {
637                                 typenode = index_types.OwnerDocument.CreateElement("Type");
638                                 typenode.SetAttribute("Name", typename);
639                                 nsnode.AppendChild(typenode);
640                         }
641                         if (typename != doc_typename)
642                                 typenode.SetAttribute("DisplayName", doc_typename);
643                         else
644                                 typenode.RemoveAttribute("DisplayName");
645                         typenode.SetAttribute ("Kind", GetTypeKind (type));
646                                 
647                         // Ensure the namespace index file exists
648                         string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
649                         string nsdoc  = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
650                         if (File.Exists (onsdoc)) {
651                                 File.Move (onsdoc, nsdoc);
652                         }
653
654                         if (!File.Exists (nsdoc)) {
655                                 Console.WriteLine("New Namespace File: " + type.Namespace);
656                                 WriteNamespaceStub(type.Namespace, dest);
657                         }
658
659                         // mark the file as corresponding to a type
660                         goodfiles[reltypepath] = goodfiles;
661                 }
662         }
663
664         class DocsTypeInfo {
665                 public Type Type;
666                 public XmlReader EcmaDocs;
667
668                 public DocsTypeInfo (Type type, XmlReader docs)
669                 {
670                         this.Type = type;
671                         this.EcmaDocs = docs;
672                 }
673         }
674
675         static 
676 #if NET_1_0
677                 IEnumerable
678 #else
679                 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo>
680 #endif
681                 GetTypes (Assembly assembly, string[] forTypes)
682         {
683                 Hashtable seen = null;
684                 if (forTypes != null)
685                         Array.Sort (forTypes);
686                 if (ecmadocs != null) {
687                         seen = new Hashtable ();
688                         int typeDepth = -1;
689                         while (ecmadocs.Read ()) {
690                                 switch (ecmadocs.Name) {
691                                         case "Type": {
692                                                 if (typeDepth == -1)
693                                                         typeDepth = ecmadocs.Depth;
694                                                 if (ecmadocs.NodeType != XmlNodeType.Element)
695                                                         continue;
696                                                 if (typeDepth != ecmadocs.Depth) // nested <Type/> element?
697                                                         continue;
698                                                 string typename = ecmadocs.GetAttribute ("FullName");
699                                                 string typename2 = GetTypeFileName (typename);
700                                                 if (forTypes != null && 
701                                                                 Array.BinarySearch (forTypes, typename) < 0 &&
702                                                                 typename != typename2 &&
703                                                                 Array.BinarySearch (forTypes, typename2) < 0)
704                                                         continue;
705                                                 Type t;
706                                                 if ((t = assembly.GetType (typename, false)) == null &&
707                                                                 (t = assembly.GetType (typename2, false)) == null)
708                                                         continue;
709                                                 seen.Add (typename, "");
710                                                 if (typename != typename2)
711                                                         seen.Add (typename2, "");
712                                                 Console.WriteLine ("  Import: {0}", t.FullName);
713                                                 yield return new DocsTypeInfo (t, ecmadocs);
714                                                 break;
715                                         }
716                                         default:
717                                                 break;
718                                 }
719                         }
720                 }
721                 foreach (Type type in assembly.GetTypes()) {
722                         if (forTypes != null && Array.BinarySearch (forTypes, type.FullName) < 0)
723                                 continue;
724                         if (seen != null && seen.ContainsKey (type.FullName))
725                                 continue;
726                         yield return new DocsTypeInfo (type, null);
727                 }
728         }
729
730         private static void SortIndexEntries (XmlElement indexTypes)
731         {
732                 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
733                 XmlNodeComparer c = new AttributeNameComparer ();
734                 SortXmlNodes (indexTypes, namespaces, c);
735
736                 for (int i = 0; i < namespaces.Count; ++i)
737                         SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
738         }
739
740         private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
741         {
742                 MyXmlNodeList l = new MyXmlNodeList (children.Count);
743                 for (int i = 0; i < children.Count; ++i)
744                         l.Add (children [i]);
745                 l.Sort (comparer);
746                 for (int i = l.Count - 1; i > 0; --i) {
747                         parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
748                 }
749         }
750
751         abstract class XmlNodeComparer : IComparer 
752 #if !NET_1_0
753                 , IComparer<XmlNode>
754 #endif
755         {
756                 public abstract int Compare (XmlNode x, XmlNode y);
757
758                 public int Compare (object x, object y)
759                 {
760                         return Compare ((XmlNode) x, (XmlNode) y);
761                 }
762         }
763
764         class AttributeNameComparer : XmlNodeComparer {
765                 public override int Compare (XmlNode x, XmlNode y)
766                 {
767                         return x.Attributes ["Name"].Value.CompareTo (y.Attributes ["Name"].Value);
768                 }
769         }
770         
771         class VersionComparer : XmlNodeComparer {
772                 public override int Compare (XmlNode x, XmlNode y)
773                 {
774                         // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
775                         string a = GetVersion (x.InnerText);
776                         string b = GetVersion (y.InnerText);
777                         return new Version (a).CompareTo (new Version (b));
778                 }
779
780                 static string GetVersion (string v)
781                 {
782                         int n = v.IndexOf ("x");
783                         if (n < 0)
784                                 return v;
785                         return v.Substring (0, n-1);
786                 }
787         }
788
789         private static string GetTypeKind (Type type)
790         {
791                 if (type.IsEnum)
792                         return "Enumeration";
793                 if (type.IsValueType)
794                         return "Structure";
795                 if (type.IsInterface)
796                         return "Interface";
797                 if (IsDelegate (type))
798                         return "Delegate";
799                 if (type.IsClass || type == typeof(System.Enum))
800                         return "Class";
801                 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
802         }
803
804         private static bool IsPublic (Type type)
805         {
806                 Type decl = type;
807                 while (decl != null) {
808                         if (!(decl.IsPublic || decl.IsNestedPublic)) {
809                                 return false;
810                         }
811                         decl = decl.DeclaringType;
812                 }
813                 return true;
814         }
815
816         private static void CleanupFiles (string dest, Hashtable goodfiles)
817         {
818                 // Look for files that no longer correspond to types
819                 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
820                         foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
821                                 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
822                                 if (!goodfiles.ContainsKey(relTypeFile)) {
823                                         XmlDocument doc = new XmlDocument ();
824                                         doc.Load (typefile.FullName);
825                                         XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
826                                         if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
827                                                 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
828                                                         WriteXml(doc.DocumentElement, writer);
829                                                 goodfiles [relTypeFile] = goodfiles;
830                                                 continue;
831                                         }
832                                         string newname = typefile.FullName + ".remove";
833                                         try { System.IO.File.Delete(newname); } catch (Exception) { }
834                                         try { typefile.MoveTo(newname); } catch (Exception) { }                                 
835                                         Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
836                                 }
837                         }
838                 }
839         }
840
841         private static TextWriter OpenWrite (string path, FileMode mode)
842         {
843                 return new StreamWriter (
844                         new FileStream (path, mode),
845                         new UTF8Encoding (false)
846                 );
847         }
848
849         private static string[] GetAssemblyVersions ()
850         {
851                 StringList versions = new StringList (assemblies.Length);
852                 for (int i = 0; i < assemblies.Length; ++i)
853                         versions.Add (GetAssemblyVersion (assemblies [i]));
854                 return DocUtils.ToStringArray (versions);
855         }
856                 
857         private static void CleanupIndexTypes (XmlElement index_types, Hashtable goodfiles)
858         {
859                 // Look for type nodes that no longer correspond to types
860                 MyXmlNodeList remove = new MyXmlNodeList ();
861                 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
862                         string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
863                         if (!goodfiles.ContainsKey(fulltypename)) {
864                                 remove.Add (typenode);
865                         }
866                 }
867                 foreach (XmlNode n in remove)
868                         n.ParentNode.RemoveChild (n);
869         }
870
871         private static void CleanupExtensions (XmlElement index_types)
872         {
873                 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
874                 if (extensionMethods.Count == 0) {
875                         if (e == null)
876                                 return;
877                         index_types.RemoveChild (e);
878                         return;
879                 }
880                 if (e == null) {
881                         e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
882                         index_types.SelectSingleNode ("/Overview").AppendChild (e);
883                 }
884                 else
885                         e.RemoveAll ();
886                 extensionMethods.Sort (DefaultExtensionMethodComparer);
887                 foreach (XmlNode m in extensionMethods) {
888                         e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
889                 }
890         }
891
892         class ExtensionMethodComparer : XmlNodeComparer {
893                 public override int Compare (XmlNode x, XmlNode y)
894                 {
895                         XmlNode xLink = x.SelectSingleNode ("Member/Link");
896                         XmlNode yLink = y.SelectSingleNode ("Member/Link");
897
898                         int n = xLink.Attributes ["Type"].Value.CompareTo (
899                                         yLink.Attributes ["Type"].Value);
900                         if (n != 0)
901                                 return n;
902                         n = xLink.Attributes ["Member"].Value.CompareTo (
903                                         yLink.Attributes ["Member"].Value);
904                         if (n == 0 && !object.ReferenceEquals (x, y))
905                                 throw new InvalidOperationException ("Duplicate extension method found!");
906                         return n;
907                 }
908         }
909
910         static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
911                 
912         public static void DoUpdateType2(string message, XmlDocument basefile, Type type, string output, bool insertSince, XmlReader ecmaDocsType) {
913                 Console.WriteLine(message + ": " + type.FullName);
914                 
915                 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
916
917                 // Update type metadata
918                 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
919
920                 if (ecmaDocsType != null) {
921                         while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
922                                 // do nothing
923                         }
924                         if (ecmaDocsType.IsEmptyElement)
925                                 ecmaDocsType = null;
926                 }
927
928                 // Update existing members.  Delete member nodes that no longer should be there,
929                 // and remember what members are already documented so we don't add them again.
930                 if (!ignoremembers) {
931                         MyXmlNodeList todelete = new MyXmlNodeList ();
932                         foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
933                                 XmlElement oldmember  = info.Node;
934                                 MemberInfo oldmember2 = info.Member;
935                                 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
936
937                                 // Interface implementations and overrides are deleted from the docs
938                                 // unless the overrides option is given.
939                                 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
940                                         oldmember2 = null;
941                                 
942                                 // Deleted (or signature changed)
943                                 if (oldmember2 == null) {
944                                         if (UpdateAssemblyVersions (oldmember, new string[]{GetAssemblyVersion (type.Assembly)}, false))
945                                                 continue;
946                                         DeleteMember ("Member Removed", output, oldmember, todelete);
947                                         continue;
948                                 }
949                                 
950                                 // Duplicated
951                                 if (seenmembers.ContainsKey (sig)) {
952                                         if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
953                                                 // ignore, already seen
954                                         }
955                                         else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
956                                                 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
957                                         else
958                                                 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
959                                         continue;
960                                 }
961                                 
962                                 // Update signature information
963                                 UpdateMember(info);
964                                 
965                                 seenmembers.Add (sig, oldmember);
966                         }
967                         foreach (XmlElement oldmember in todelete)
968                                 oldmember.ParentNode.RemoveChild (oldmember);
969                 }
970                 
971                 if (!IsDelegate(type) && !ignoremembers) {
972                         XmlNode members = WriteElement (basefile.DocumentElement, "Members");
973                         foreach (MemberInfo m in Sort (type.GetMembers(DefaultBindingFlags))) {
974                                 if (m is Type) continue;
975                                 
976                                 string sig = MakeMemberSignature(m);
977                                 if (sig == null) continue;
978                                 if (seenmembers.ContainsKey(sig)) continue;
979                                 
980                                 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
981                                 // are not added in.
982                                 if (!IsNew(m)) continue;
983                                 
984                                 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
985                                 if (mm == null) continue;
986                                 members.AppendChild( mm );
987         
988                                 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
989                                 additions++;
990                         }
991                 }
992                 
993                 // Import code snippets from files
994                 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
995                         if (!(code is XmlElement)) continue;
996                         string file = ((XmlElement)code).GetAttribute("src");
997                         string lang = ((XmlElement)code).GetAttribute("lang");
998                         if (file != "") {
999                                 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1000                                 if (src != null)
1001                                         code.InnerText = src;
1002                         }
1003                 }
1004
1005                 if (insertSince && since != null) {
1006                         XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1007                         docs.AppendChild (CreateSinceNode (basefile));
1008                 }
1009
1010                 do {
1011                         XmlElement d = basefile.DocumentElement ["Docs"];
1012                         XmlElement m = basefile.DocumentElement ["Members"];
1013                         if (d != null && m != null)
1014                                 basefile.DocumentElement.InsertBefore (
1015                                                 basefile.DocumentElement.RemoveChild (d), m);
1016                         SortTypeMembers (m);
1017                 } while (false);
1018
1019                 System.IO.TextWriter writer;
1020                 if (output == null)
1021                         writer = Console.Out;
1022                 else {
1023                         FileInfo file = new FileInfo (output);
1024                         if (!file.Directory.Exists) {
1025                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1026                                 file.Directory.Create ();
1027                         }
1028                         writer = OpenWrite (output, FileMode.Create);
1029                 }
1030
1031                 using (writer)
1032                         WriteXml(basefile.DocumentElement, writer);
1033         }
1034
1035         private static string GetCodeSource (string lang, string file)
1036         {
1037                 int anchorStart;
1038                 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1039                         // Grab the specified region
1040                         string region = "#region " + file.Substring (anchorStart + 4);
1041                         file          = file.Substring (0, anchorStart + 3);
1042                         try {
1043                                 using (StreamReader reader = new StreamReader (file)) {
1044                                         string line;
1045                                         StringBuilder src = new StringBuilder ();
1046                                         int indent = -1;
1047                                         while ((line = reader.ReadLine ()) != null) {
1048                                                 if (line.Trim() == region) {
1049                                                         indent = line.IndexOf (region);
1050                                                         continue;
1051                                                 }
1052                                                 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1053                                                         break;
1054                                                 }
1055                                                 if (indent >= 0)
1056                                                         src.Append (
1057                                                                         (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1058                                                                         "\n");
1059                                         }
1060                                         return src.ToString ();
1061                                 }
1062                         } catch (Exception e) {
1063                                 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
1064                                                 file, region, show_exceptions ? e.ToString () : e.Message);
1065                                 return null;
1066                         }
1067                 }
1068                 try {
1069                         using (StreamReader reader = new StreamReader (file))
1070                                 return reader.ReadToEnd ();
1071                 } catch (Exception e) {
1072                         Error ("Could not load <code/> file '" + file + "': " + e.Message);
1073                 }
1074                 return null;
1075         }
1076
1077         private static 
1078 #if NET_1_0
1079                 IEnumerable
1080 #else
1081                 IEnumerable<DocsNodeInfo>
1082 #endif
1083                 GetDocumentationMembers (XmlDocument basefile, Type type, XmlReader ecmaDocsMembers)
1084         {
1085                 if (ecmaDocsMembers != null) {
1086                         int membersDepth = ecmaDocsMembers.Depth;
1087                         bool go = true;
1088                         while (go && ecmaDocsMembers.Read ()) {
1089                                 switch (ecmaDocsMembers.Name) {
1090                                         case "Member": {
1091                                                 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1092                                                         continue;
1093                                                 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1094                                                 string xp = GetXPathForMember (dm);
1095                                                 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1096                                                 MemberInfo m;
1097                                                 if (oldmember == null) {
1098                                                         m = GetMember (type, dm);
1099                                                         if (m == null) {
1100                                                                 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1101                                                                                 type.FullName, dm.MemberSignatures ["C#"]);
1102                                                                                 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1103                                                                 continue;
1104                                                         }
1105                                                         // oldmember lookup may have failed due to type parameter renames.
1106                                                         // Try again.
1107                                                         oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1108                                                         if (oldmember == null) {
1109                                                                 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1110                                                                 oldmember = basefile.CreateElement ("Member");
1111                                                                 oldmember.SetAttribute ("MemberName", dm.MemberName);
1112                                                                 members.AppendChild (oldmember);
1113                                                                 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1114                                                                         XmlElement ms = basefile.CreateElement ("MemberSignature");
1115                                                                         ms.SetAttribute ("Language", key);
1116                                                                         ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1117                                                                         oldmember.AppendChild (ms);
1118                                                                 }
1119                                                                 oldmember.SetAttribute ("__monodocer-seen__", "true");
1120                                                                 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1121                                                                 additions++;
1122                                                         }
1123                                                 }
1124                                                 else {
1125                                                         m = GetMember (type, new DocumentationMember (oldmember));
1126                                                         if (m == null) {
1127                                                                 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1128                                                                                 type.FullName, dm.MemberSignatures ["C#"]);
1129                                                                 continue;
1130                                                         }
1131                                                         oldmember.SetAttribute ("__monodocer-seen__", "true");
1132                                                 }
1133                                                 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1134                                                 if (ecmaDocsMembers.Name != "Docs")
1135                                                         throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1136                                                 node.EcmaDocs = ecmaDocsMembers;
1137                                                 yield return node;
1138                                                 break;
1139                                         }
1140                                         case "Members":
1141                                                 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1142                                                         go = false;
1143                                                 }
1144                                                 break;
1145                                 }
1146                         }
1147                 }
1148                 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1149                         if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1150                                 oldmember.RemoveAttribute ("__monodocer-seen__");
1151                                 continue;
1152                         }
1153                         MemberInfo m = GetMember (type, new DocumentationMember (oldmember));
1154                         if (m == null) {
1155                                 yield return new DocsNodeInfo (oldmember);
1156                         }
1157                         else {
1158                                 yield return new DocsNodeInfo (oldmember, m);
1159                         }
1160                 }
1161         }
1162
1163         static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1164         {
1165                 string format = output != null
1166                         ? "{0}: File='{1}'; Signature='{4}'"
1167                         : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1168                 Error (format,
1169                                 reason, 
1170                                 output,
1171                                 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1172                                 member.Attributes ["MemberName"].Value, 
1173                                 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1174                 if (!delete && MemberDocsHaveUserContent (member)) {
1175                         Error ("Member deletions must be enabled with the --delete option.");
1176                 } else {
1177                         todelete.Add (member);
1178                         deletions++;
1179                 }
1180         }
1181
1182         class MemberComparer : XmlNodeComparer {
1183                 public override int Compare (XmlNode x, XmlNode y)
1184                 {
1185                         int r;
1186                         string xMemberName = x.Attributes ["MemberName"].Value;
1187                         string yMemberName = y.Attributes ["MemberName"].Value;
1188
1189                         // generic methods *end* with '>'
1190                         // it's possible for explicitly implemented generic interfaces to
1191                         // contain <...> without being a generic method
1192                         if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1193                                         (r = xMemberName.CompareTo (yMemberName)) != 0)
1194                                 return r;
1195
1196                         int lt;
1197                         if ((lt = xMemberName.IndexOf ("<")) >= 0)
1198                                 xMemberName = xMemberName.Substring (0, lt);
1199                         if ((lt = yMemberName.IndexOf ("<")) >= 0)
1200                                 yMemberName = yMemberName.Substring (0, lt);
1201                         if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1202                                 return r;
1203
1204                         // if @MemberName matches, then it's either two different types of
1205                         // members sharing the same name, e.g. field & property, or it's an
1206                         // overloaded method.
1207                         // for different type, sort based on MemberType value.
1208                         r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1209                                         y.SelectSingleNode ("MemberType").InnerText);
1210                         if (r != 0)
1211                                 return r;
1212
1213                         // same type -- must be an overloaded method.  Sort based on type 
1214                         // parameter count, then parameter count, then by the parameter 
1215                         // type names.
1216                         XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1217                         XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1218                         if (xTypeParams.Count != yTypeParams.Count)
1219                                 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1220                         for (int i = 0; i < xTypeParams.Count; ++i) {
1221                                 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1222                                                 yTypeParams [i].Attributes ["Name"].Value);
1223                                 if (r != 0)
1224                                         return r;
1225                         }
1226
1227                         XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1228                         XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1229                         if (xParams.Count != yParams.Count)
1230                                 return xParams.Count <= yParams.Count ? -1 : 1;
1231                         for (int i = 0; i < xParams.Count; ++i) {
1232                                 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1233                                                 yParams [i].Attributes ["Type"].Value);
1234                                 if (r != 0)
1235                                         return r;
1236                         }
1237                         // all parameters match, but return value might not match if it was
1238                         // changed between one version and another.
1239                         XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1240                         XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1241                         if (xReturn != null && yReturn != null) {
1242                                 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1243                                 if (r != 0)
1244                                         return r;
1245                         }
1246
1247                         return 0;
1248                 }
1249         }
1250
1251         static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1252
1253         private static void SortTypeMembers (XmlNode members)
1254         {
1255                 if (members == null)
1256                         return;
1257                 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1258         }
1259         
1260         private static bool MemberDocsHaveUserContent (XmlNode e)
1261         {
1262                 e = (XmlElement)e.SelectSingleNode("Docs");
1263                 if (e == null) return false;
1264                 foreach (XmlElement d in e.SelectNodes("*"))
1265                         if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1266                                 return true;
1267                 return false;
1268         }
1269         
1270         private static bool IsNew(MemberInfo m) {
1271                 if (!nooverrides) return true;
1272                 if (m is MethodInfo && !IsNew((MethodInfo)m)) return false;
1273                 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetGetMethod())) return false;
1274                 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetSetMethod())) return false;
1275                 if (m is EventInfo && !IsNew(((EventInfo)m).GetAddMethod(true))) return false;
1276                 if (m is EventInfo && !IsNew(((EventInfo)m).GetRaiseMethod())) return false;
1277                 if (m is EventInfo && !IsNew(((EventInfo)m).GetRemoveMethod())) return false;
1278                 return true;
1279         }
1280         
1281         private static bool IsNew(MethodInfo m) {
1282                 if (m == null) return true;
1283                 MethodInfo b = m.GetBaseDefinition();
1284                 if (b == null || b == m) return true;
1285                 return false;
1286         }
1287         
1288         // UPDATE HELPER FUNCTIONS      
1289         
1290 #if false
1291         private static XmlElement FindMatchingMember(Type type, XmlElement newfile, XmlElement oldmember) {
1292                 MemberInfo oldmember2 = GetMember(type, oldmember.CreateNavigator ());
1293                 if (oldmember2 == null) return null;
1294                 
1295                 string membername = oldmember.GetAttribute("MemberName");
1296                 foreach (XmlElement newmember in newfile.SelectNodes("Members/Member[@MemberName='" + membername + "']")) {
1297                         if (GetMember(type, newmember.CreateNavigator ()) == oldmember2) return newmember;
1298                 }
1299                 
1300                 return null;
1301         }
1302 #endif
1303         
1304         private static MemberInfo GetMember(Type type, DocumentationMember member) {
1305                 string membertype = member.MemberType;
1306                 
1307                 string returntype = member.ReturnType;
1308                 
1309                 string docName = member.MemberName;
1310                 string[] docTypeParams = GetTypeParameters (docName);
1311
1312                 // Loop through all members in this type with the same name
1313                 foreach (MemberInfo mi in GetReflectionMembers (type, docName)) {
1314                         if (mi is Type) continue;
1315                         if (GetMemberType(mi) != membertype) continue;
1316
1317                         string sig = MakeMemberSignature(mi);
1318                         if (sig == null) continue; // not publicly visible
1319
1320                         ParameterInfo[] pis = null;
1321                         string[] typeParams = null;
1322                         if (mi is MethodInfo || mi is ConstructorInfo) {
1323                                 MethodBase mb = (MethodBase) mi;
1324                                 pis = mb.GetParameters();
1325                                 if (docTypeParams != null && DocUtils.GetContainsGenericParameters (mb)) {
1326                                         Type[] args = DocUtils.GetGenericArguments (mb);
1327                                         if (args.Length == docTypeParams.Length) {
1328                                                 typeParams = new string [args.Length];
1329                                                 for (int i = 0; i < args.Length; ++i)
1330                                                         typeParams [i] = args [i].Name;
1331                                         }
1332                                 }
1333                         }
1334                         else if (mi is PropertyInfo)
1335                                 pis = ((PropertyInfo)mi).GetIndexParameters();
1336                         
1337                         int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1338                         int pcount = pis == null ? 0 : pis.Length;
1339                         if (mcount != pcount)
1340                                 continue;
1341                         
1342                         if (mi is MethodInfo) {
1343                                 // Casting operators can overload based on return type.
1344                                 if (returntype != GetReplacedString (
1345                                                         GetDocTypeFullName (((MethodInfo)mi).ReturnType), 
1346                                                         typeParams, docTypeParams)) {
1347                                         continue;
1348                                 }
1349                         }
1350
1351                         if (pcount == 0)
1352                                 return mi;
1353                         bool good = true;
1354                         for (int i = 0; i < pis.Length; i++) {
1355                                 string paramType = GetReplacedString (
1356                                         GetDocParameterType (pis [i].ParameterType),
1357                                         typeParams, docTypeParams);
1358                                 if (paramType != (string) member.Parameters [i]) {
1359                                         good = false;
1360                                         break;
1361                                 }
1362                         }
1363                         if (!good) continue;
1364
1365                         return mi;
1366                 }
1367                 
1368                 return null;
1369         }
1370
1371         private static MemberInfoEnumerable GetReflectionMembers (Type type, string docName)
1372         {
1373                 // need to worry about 4 forms of //@MemberName values:
1374                 //  1. "Normal" (non-generic) member names: GetEnumerator
1375                 //    - Lookup as-is.
1376                 //  2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1377                 //    - try as-is, and try type.member (due to "kludge" for property
1378                 //      support.
1379                 //  3. "Normal" Generic member names: Sort<T> (CSC)
1380                 //    - need to remove generic parameters --> "Sort"
1381                 //  4. Explicitly-implemented interface members for generic interfaces: 
1382                 //    -- System.Collections.Generic.IEnumerable<T>.Current
1383                 //    - Try as-is, and try type.member, *keeping* the generic parameters.
1384                 //     --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1385                 //  5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1386                 //    'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1387                 //    this as (1) or (2).
1388                 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1389                         // Cases 1 & 2
1390                         foreach (MemberInfo mi in type.GetMember (docName, DefaultBindingFlags))
1391                                 yield return mi;
1392                         if (CountChars (docName, '.') > 0)
1393                                 // might be a property; try only type.member instead of
1394                                 // namespace.type.member.
1395                                 foreach (MemberInfo mi in 
1396                                                 type.GetMember (DocUtils.GetTypeDotMember (docName), DefaultBindingFlags))
1397                                         yield return mi;
1398                         yield break;
1399                 }
1400                 // cases 3 & 4
1401                 int numLt = 0;
1402                 int numDot = 0;
1403                 int startLt, startType, startMethod;
1404                 startLt = startType = startMethod = -1;
1405                 for (int i = 0; i < docName.Length; ++i) {
1406                         switch (docName [i]) {
1407                                 case '<':
1408                                         if (numLt == 0) {
1409                                                 startLt = i;
1410                                         }
1411                                         ++numLt;
1412                                         break;
1413                                 case '>':
1414                                         --numLt;
1415                                         if (numLt == 0 && (i + 1) < docName.Length)
1416                                                 // there's another character in docName, so this <...> sequence is
1417                                                 // probably part of a generic type -- case 4.
1418                                                 startLt = -1;
1419                                         break;
1420                                 case '.':
1421                                         startType = startMethod;
1422                                         startMethod = i;
1423                                         ++numDot;
1424                                         break;
1425                         }
1426                 }
1427                 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1428                 // case 3
1429                 foreach (MemberInfo mi in type.GetMember (refName, DefaultBindingFlags))
1430                         yield return mi;
1431
1432                 // case 4
1433                 foreach (MemberInfo mi in type.GetMember (refName.Substring (startType + 1), DefaultBindingFlags))
1434                         yield return mi;
1435
1436                 // If we _still_ haven't found it, we've hit another generic naming issue:
1437                 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1438                 // explicitly-implemented METHOD names (not properties), e.g. 
1439                 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1440                 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1441                 // which the XML docs will contain.
1442                 //
1443                 // Alas, we can't derive the Mono name from docName, so we need to iterate
1444                 // over all member names, convert them into CSC format, and compare... :-(
1445                 if (numDot == 0)
1446                         yield break;
1447                 foreach (MemberInfo mi in type.GetMembers (DefaultBindingFlags)) {
1448                         if (GetMemberName (mi) == docName)
1449                                 yield return mi;
1450                 }
1451         }
1452
1453         static string[] GetTypeParameters (string docName)
1454         {
1455                 if (docName [docName.Length-1] != '>')
1456                         return null;
1457                 StringList types = new StringList ();
1458                 int endToken = docName.Length-2;
1459                 int i = docName.Length-2;
1460                 do {
1461                         if (docName [i] == ',' || docName [i] == '<') {
1462                                 types.Add (docName.Substring (i + 1, endToken - i));
1463                                 endToken = i-1;
1464                         }
1465                         if (docName [i] == '<')
1466                                 break;
1467                 } while (--i >= 0);
1468
1469                 types.Reverse ();
1470                 return DocUtils.ToStringArray (types);
1471         }
1472
1473         static string GetReplacedString (string typeName, string[] from, string[] to)
1474         {
1475                 if (from == null)
1476                         return typeName;
1477                 for (int i = 0; i < from.Length; ++i)
1478                         typeName = typeName.Replace (from [i], to [i]);
1479                 return typeName;
1480         }
1481         
1482         // CREATE A STUB DOCUMENTATION FILE     
1483
1484         public static XmlElement StubType(Type type, string output, XmlReader ecmaDocsType) {
1485                 string typesig = MakeTypeSignature(type);
1486                 if (typesig == null) return null; // not publicly visible
1487                 
1488                 XmlDocument doc = new XmlDocument();
1489                 XmlElement root = doc.CreateElement("Type");
1490                 doc.AppendChild (root);
1491
1492                 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1493                 
1494                 return root;
1495         }
1496
1497         private static XmlElement CreateSinceNode (XmlDocument doc)
1498         {
1499                 XmlElement s = doc.CreateElement ("since");
1500                 s.SetAttribute ("version", since);
1501                 return s;
1502         }
1503         
1504         // STUBBING/UPDATING FUNCTIONS
1505         
1506         public static void UpdateType(XmlElement root, Type type, XmlReader ecmaDocsType) {
1507                 root.SetAttribute("Name", GetDocTypeName (type));
1508                 root.SetAttribute("FullName", GetDocTypeFullName (type));
1509
1510                 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1511                 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1512                 
1513                 XmlElement ass = WriteElement(root, "AssemblyInfo");
1514                 WriteElementText(ass, "AssemblyName", type.Assembly.GetName().Name);
1515                 UpdateAssemblyVersions(root, type, true);
1516                 if (type.Assembly.GetName().CultureInfo.Name != "")
1517                         WriteElementText(ass, "AssemblyCulture", type.Assembly.GetName().CultureInfo.Name);
1518                 else
1519                         ClearElement(ass, "AssemblyCulture");
1520                 
1521                 // Why-oh-why do we put assembly attributes in each type file?
1522                 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1523                 // since they're outdated in current docs, and a waste of space.
1524                 //MakeAttributes(ass, type.Assembly, true);
1525                 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1526                 if (assattrs != null)
1527                         ass.RemoveChild(assattrs);
1528                 
1529                 NormalizeWhitespace(ass);
1530                 
1531                 if (DocUtils.IsGenericType (type)) {
1532                         MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1533                 } else {
1534                         ClearElement(root, "TypeParameters");
1535                 }
1536                 
1537                 if (type.BaseType != null) {
1538                         XmlElement basenode = WriteElement(root, "Base");
1539                         
1540                         string basetypename = GetDocTypeFullName (type.BaseType);
1541                         if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1542                         WriteElementText(root, "Base/BaseTypeName", basetypename);
1543                         
1544                         // Document how this type instantiates the generic parameters of its base type
1545                         if (DocUtils.IsGenericType (type.BaseType)) {
1546                                 ClearElement(basenode, "BaseTypeArguments");
1547                                 Type[] baseGenArgs     = DocUtils.GetGenericArguments (type.BaseType);
1548                                 Type genericDefinition = DocUtils.GetGenericTypeDefinition (type.BaseType);
1549                                 Type[] genTypeDefArgs  = DocUtils.GetGenericArguments (genericDefinition);
1550                                 for (int i = 0; i < baseGenArgs.Length; i++) {
1551                                         Type typearg   = baseGenArgs [i];
1552                                         Type typeparam = genTypeDefArgs [i];
1553                                         
1554                                         XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1555                                         XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1556                                         bta.AppendChild(arg);
1557                                         arg.SetAttribute("TypeParamName", typeparam.Name);
1558                                         arg.InnerText = GetDocTypeFullName (typearg);
1559                                 }
1560                         }
1561                 } else {
1562                         ClearElement(root, "Base");
1563                 }
1564
1565                 if (!IsDelegate(type) && !type.IsEnum) {
1566                         // Get a sorted list of interface implementations.  Don't include
1567                         // interfaces that are implemented by a base type or another interface
1568                         // because they go on the base type or base interface's signature.
1569                         ArrayList interface_names = new ArrayList();
1570                         foreach (Type i in type.GetInterfaces())
1571                                 if ((type.BaseType == null || Array.IndexOf(type.BaseType.GetInterfaces(), i) == -1) && InterfaceNotFromAnother(i, type.GetInterfaces()))
1572                                         interface_names.Add(GetDocTypeFullName (i));
1573                         interface_names.Sort();
1574
1575                         XmlElement interfaces = WriteElement(root, "Interfaces");
1576                         interfaces.RemoveAll();
1577                         foreach (string iname in interface_names) {
1578                                 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1579                                 interfaces.AppendChild(iface);
1580                                 WriteElementText(iface, "InterfaceName", iname);
1581                         }
1582                 } else {
1583                         ClearElement(root, "Interfaces");
1584                 }
1585
1586                 MakeAttributes(root, type, false);
1587                 
1588                 if (IsDelegate(type)) {
1589                         MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1590                         MakeParameters(root, type.GetMethod("Invoke").GetParameters());
1591                         MakeReturnValue(root, type.GetMethod("Invoke"));
1592                 }
1593                 
1594                 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1595                 if (ecmaDocsType != null) {
1596                         if (ecmaDocsType.Name != "Docs") {
1597                                 int depth = ecmaDocsType.Depth;
1598                                 while (ecmaDocsType.Read ()) {
1599                                         if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1600                                                 break;
1601                                 }
1602                         }
1603                         if (!ecmaDocsType.IsStartElement ("Docs"))
1604                                 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1605                         typeInfo.EcmaDocs = ecmaDocsType;
1606                 }
1607                 MakeDocNode (typeInfo);
1608                 
1609                 if (!IsDelegate (type))
1610                         WriteElement (root, "Members");
1611
1612                 NormalizeWhitespace(root);
1613         }
1614
1615         class MemberInfoComparer : IComparer
1616 #if !NET_1_0
1617                                                                                                                  , IComparer<MemberInfo>
1618 #endif
1619         {
1620                 public int Compare (MemberInfo x, MemberInfo y)
1621                 {
1622                         string xs = slashdocFormatter.GetName (x);
1623                         string ys = slashdocFormatter.GetName (y);
1624                         // return String.Compare (xs, ys, StringComparison.OrdinalIgnoreCase);
1625                         return string.Compare (xs, ys, true, CultureInfo.InvariantCulture);
1626                 }
1627
1628                 public int Compare (object x, object y)
1629                 {
1630                         return Compare ((MemberInfo) x, (MemberInfo) y);
1631                 }
1632         }
1633
1634         static MemberInfoComparer memberInfoComparer = new MemberInfoComparer ();
1635
1636         private static MemberInfo[] Sort (MemberInfo[] members)
1637         {
1638 #if NET_1_0
1639                 ArrayList l = new ArrayList ();
1640                 l.AddRange (members);
1641                 l.Sort (memberInfoComparer);
1642                 return (MemberInfo[]) l.ToArray (typeof(MemberInfo));
1643 #else
1644                 Array.Sort (members, memberInfoComparer);
1645                 return members;
1646 #endif
1647         }
1648
1649         static IEnumerable Sort (IEnumerable list)
1650         {
1651                 ArrayList l = new ArrayList (list as ICollection);
1652                 l.Sort ();
1653                 return l;
1654         }
1655
1656 #if !NET_1_0
1657         static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1658         {
1659                 List<T> l = new List<T> (list);
1660                 l.Sort ();
1661                 return l;
1662         }
1663 #endif
1664         
1665         private static void UpdateMember(DocsNodeInfo info) {   
1666                 XmlElement me = (XmlElement) info.Node;
1667                 MemberInfo mi = info.Member;
1668                 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1669                 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1670
1671                 WriteElementText(me, "MemberType", GetMemberType(mi));
1672                 
1673                 UpdateAssemblyVersions(me, mi, true);
1674                 MakeAttributes(me, mi, false);
1675                 MakeReturnValue(me, mi);
1676                 if (mi is MethodBase) {
1677                         MethodBase mb = (MethodBase) mi;
1678                         if (DocUtils.GetContainsGenericParameters (mb))
1679                                 MakeTypeParameters (me, DocUtils.GetGenericArguments (mb));
1680                 }
1681                 MakeParameters(me, mi);
1682                 
1683                 string fieldValue;
1684                 if (mi is FieldInfo && GetFieldConstValue((FieldInfo)mi, out fieldValue))
1685                         WriteElementText(me, "MemberValue", fieldValue);
1686                 
1687                 info.Node = WriteElement (me, "Docs");
1688                 MakeDocNode (info);
1689                 UpdateExtensionMethods (me, info);
1690         }
1691
1692         static readonly string[] ValidExtensionMembers = {
1693                 "Docs",
1694                 "MemberSignature",
1695                 "MemberType",
1696                 "Parameters",
1697                 "ReturnValue",
1698                 "TypeParameters",
1699         };
1700
1701         static readonly string[] ValidExtensionDocMembers = {
1702                 "param",
1703                 "summary",
1704                 "typeparam",
1705         };
1706
1707         private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1708         {
1709                 MethodInfo me = info.Member as MethodInfo;
1710                 if (me == null)
1711                         return;
1712                 if (info.Parameters.Length < 1)
1713                         return;
1714                 if (!DocUtils.IsExtensionMethod (me))
1715                         return;
1716
1717                 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1718                 XmlNode member = e.CloneNode (true);
1719                 em.AppendChild (member);
1720                 RemoveExcept (member, ValidExtensionMembers);
1721                 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1722                 WriteElementText (member, "MemberType", "ExtensionMethod");
1723                 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1724                 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1725                 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1726                 member.AppendChild (link);
1727                 AddTargets (em, info);
1728
1729                 extensionMethods.Add (em);
1730         }
1731
1732         private static void RemoveExcept (XmlNode node, string[] except)
1733         {
1734                 if (node == null)
1735                         return;
1736                 MyXmlNodeList remove = null;
1737                 foreach (XmlNode n in node.ChildNodes) {
1738                         if (Array.BinarySearch (except, n.Name) < 0) {
1739                                 if (remove == null)
1740                                         remove = new MyXmlNodeList ();
1741                                 remove.Add (n);
1742                         }
1743                 }
1744                 if (remove != null)
1745                         foreach (XmlNode n in remove)
1746                                 node.RemoveChild (n);
1747         }
1748
1749         private static void AddTargets (XmlNode member, DocsNodeInfo info)
1750         {
1751                 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1752                 member.PrependChild (targets);
1753                 if (!DocUtils.IsGenericParameter (info.Parameters [0].ParameterType))
1754                         AppendElementAttributeText (targets, "Target", "Type",
1755                                 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1756                 else {
1757                         Type[] constraints = DocUtils.GetGenericParameterConstraints (
1758                                 info.Parameters [0].ParameterType);
1759                         if (constraints.Length == 0)
1760                                 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1761                         else
1762                                 foreach (Type c in constraints)
1763                                         AppendElementAttributeText(targets, "Target", "Type",
1764                                                 slashdocFormatter.GetDeclaration (c));
1765                 }
1766         }
1767         
1768         private static bool GetFieldConstValue(FieldInfo field, out string value) {
1769                 value = null;
1770                 if (field.DeclaringType.IsEnum) return false;
1771                 if (DocUtils.IsGenericType (field.DeclaringType)) return false;
1772                 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
1773                         object val;
1774                         try {
1775                                 val = field.GetValue(null);
1776                         } catch {
1777                                 return false;
1778                         }
1779                         if (val == null) value = "null";
1780                         else if (val is Enum) value = val.ToString();
1781                         else if (val is IFormattable) {
1782                                 value = ((IFormattable)val).ToString();
1783                                 if (val is string)
1784                                         value = "\"" + value + "\"";
1785                         }
1786                         if (value != null && value != "")
1787                                 return true;
1788                 }
1789                 return false;
1790         }
1791         
1792         // XML HELPER FUNCTIONS
1793         
1794         private static XmlElement WriteElement(XmlNode parent, string element) {
1795                 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1796                 if (ret == null) {
1797                         string[] path = element.Split('/');
1798                         foreach (string p in path) {
1799                                 ret = (XmlElement)parent.SelectSingleNode(p);
1800                                 if (ret == null) {
1801                                         string ename = p;
1802                                         if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1803                                                 ename = ename.Substring(0, ename.IndexOf('['));
1804                                         ret = parent.OwnerDocument.CreateElement(ename);
1805                                         parent.AppendChild(ret);
1806                                         parent = ret;
1807                                 } else {
1808                                         parent = ret;
1809                                 }
1810                         }
1811                 }
1812                 return ret;
1813         }
1814         private static void WriteElementText(XmlNode parent, string element, string value) {
1815                 XmlElement node = WriteElement(parent, element);
1816                 node.InnerText = value;
1817         }
1818
1819         static XmlElement AppendElementText (XmlNode parent, string element, string value)
1820         {
1821                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1822                 parent.AppendChild (n);
1823                 n.InnerText = value;
1824                 return n;
1825         }
1826
1827         static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1828         {
1829                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1830                 parent.AppendChild (n);
1831                 n.SetAttribute (attribute, value);
1832                 return n;
1833         }
1834
1835         private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1836         {
1837                 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1838                 dest.AppendChild (copy);
1839                 return copy;
1840         }
1841
1842         private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1843                 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1844                 if (node != null)
1845                         return;
1846                 node = WriteElement(parent, element);
1847                 node.InnerText = value;
1848         }
1849         private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1850                 XmlElement node = WriteElement(parent, element);
1851                 if (node.GetAttribute(attribute) == value) return;
1852                 node.SetAttribute(attribute, value);
1853         }
1854         private static void ClearElement(XmlElement parent, string name) {
1855                 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1856                 if (node != null)
1857                         parent.RemoveChild(node);
1858         }
1859         private static void ClearElementChildren(XmlElement parent) {
1860                 parent.RemoveAll();
1861         }
1862         
1863         // DOCUMENTATION HELPER FUNCTIONS
1864         
1865         private static void MakeDocNode (DocsNodeInfo info)
1866         {
1867                 Type[] genericParams        = info.GenericParameters;
1868                 ParameterInfo[] parameters  = info.Parameters;
1869                 Type returntype             = info.ReturnType;
1870                 bool returnisreturn         = info.ReturnIsReturn;
1871                 XmlElement e                = info.Node;
1872                 bool addremarks             = info.AddRemarks;
1873
1874                 WriteElementInitialText(e, "summary", "To be added.");
1875                 
1876                 if (parameters != null) {
1877                         string[] values = new string [parameters.Length];
1878                         for (int i = 0; i < values.Length; ++i)
1879                                 values [i] = parameters [i].Name;
1880                         UpdateParameters (e, "param", values);
1881                 }
1882
1883                 if (genericParams != null) {
1884                         string[] values = new string [genericParams.Length];
1885                         for (int i = 0; i < values.Length; ++i)
1886                                 values [i] = genericParams [i].Name;
1887                         UpdateParameters (e, "typeparam", values);
1888                 }
1889
1890                 string retnodename = null;
1891                 if (returntype != null && returntype != typeof(void)) {
1892                         retnodename = returnisreturn ? "returns" : "value";
1893                         string retnodename_other = !returnisreturn ? "returns" : "value";
1894                         
1895                         // If it has a returns node instead of a value node, change its name.
1896                         XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1897                         if (retother != null) {
1898                                 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1899                                 foreach (XmlNode node in retother)
1900                                         retnode.AppendChild(node.CloneNode(true));
1901                                 e.ReplaceChild(retnode, retother);
1902                         } else {
1903                                 WriteElementInitialText(e, retnodename, "To be added.");
1904                         }
1905                 } else {
1906                         ClearElement(e, "returns");
1907                         ClearElement(e, "value");
1908                 }
1909
1910                 if (addremarks)
1911                         WriteElementInitialText(e, "remarks", "To be added.");
1912
1913                 if (info.EcmaDocs != null) {
1914                         XmlReader r = info.EcmaDocs;
1915                         int depth = r.Depth;
1916                         r.ReadStartElement ("Docs");
1917                         while (r.Read ()) {
1918                                 if (r.Name == "Docs") {
1919                                         if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1920                                                 break;
1921                                         else
1922                                                 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1923                                 }
1924                                 if (!r.IsStartElement ())
1925                                         continue;
1926                                 switch (r.Name) {
1927                                         case "param":
1928                                         case "typeparam": {
1929                                                 XmlNode doc = e.SelectSingleNode (
1930                                                                 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1931                                                 string value = r.ReadInnerXml ();
1932                                                 if (doc != null)
1933                                                         doc.InnerXml = value.Replace ("\r", "");
1934                                                 break;
1935                                         }
1936                                         case "altmember":
1937                                         case "exception":
1938                                         case "permission":
1939                                         case "seealso": {
1940                                                 string name = r.Name;
1941                                                 string cref = r.GetAttribute ("cref");
1942                                                 XmlNode doc = e.SelectSingleNode (
1943                                                                 r.Name + "[@cref='" + cref + "']");
1944                                                 string value = r.ReadInnerXml ().Replace ("\r", "");
1945                                                 if (doc != null)
1946                                                         doc.InnerXml = value;
1947                                                 else {
1948                                                         XmlElement n = e.OwnerDocument.CreateElement (name);
1949                                                         n.SetAttribute ("cref", cref);
1950                                                         n.InnerXml = value;
1951                                                         e.AppendChild (n);
1952                                                 }
1953                                                 break;
1954                                         }
1955                                         default: {
1956                                                 string name = r.Name;
1957                                                 string xpath = r.Name;
1958                                                 StringList attributes = new StringList (r.AttributeCount);
1959                                                 if (r.MoveToFirstAttribute ()) {
1960                                                         do {
1961                                                                 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1962                                                         } while (r.MoveToNextAttribute ());
1963                                                         r.MoveToContent ();
1964                                                 }
1965                                                 if (attributes.Count > 0) {
1966                                                         xpath += "[" + string.Join (" and ", DocUtils.ToStringArray (attributes)) + "]";
1967                                                 }
1968                                                 XmlNode doc = e.SelectSingleNode (xpath);
1969                                                 string value = r.ReadInnerXml ().Replace ("\r", "");
1970                                                 if (doc != null) {
1971                                                         doc.InnerXml = value;
1972                                                 }
1973                                                 else {
1974                                                         XmlElement n = e.OwnerDocument.CreateElement (name);
1975                                                         n.InnerXml = value;
1976                                                         foreach (string a in attributes) {
1977                                                                 int eq = a.IndexOf ('=');
1978                                                                 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1979                                                         }
1980                                                         e.AppendChild (n);
1981                                                 }
1982                                                 break;
1983                                         }
1984                                 }
1985                         }
1986                 }
1987                 if (info.SlashDocs != null) {
1988                         XmlNode elem = info.SlashDocs;
1989                         if (elem != null) {
1990                                 if (elem.SelectSingleNode("summary") != null)
1991                                         ClearElement(e, "summary");
1992                                 if (elem.SelectSingleNode("remarks") != null)
1993                                         ClearElement(e, "remarks");
1994                                 if (elem.SelectSingleNode("value") != null)
1995                                         ClearElement(e, "value");
1996                                 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1997                                         ClearElement(e, retnodename);
1998
1999                                 foreach (XmlNode child in elem.ChildNodes) {
2000                                         switch (child.Name) {
2001                                                 case "param":
2002                                                 case "typeparam": {
2003                                                         XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
2004                                                         if (p2 != null)
2005                                                                 p2.InnerXml = child.InnerXml;
2006                                                         break;
2007                                                 }
2008                                                 case "altmember":
2009                                                 case "exception":
2010                                                 case "permission": {
2011                                                         XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
2012                                                         if (a == null) {
2013                                                                 a = e.OwnerDocument.CreateElement (child.Name);
2014                                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2015                                                                 e.AppendChild (a);
2016                                                         }
2017                                                         a.InnerXml = child.InnerXml;
2018                                                         break;
2019                                                 }
2020                                                 case "seealso": {
2021                                                         XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
2022                                                         if (a == null) {
2023                                                                 a = e.OwnerDocument.CreateElement ("altmember");
2024                                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2025                                                                 e.AppendChild (a);
2026                                                         }
2027                                                         break;
2028                                                 }
2029                                                 default:
2030                                                         CopyNode (child, e);
2031                                                         break;
2032                                         }
2033                                 }
2034                         }
2035                 }
2036                 
2037                 OrderDocsNodes (e, e.ChildNodes);
2038                 NormalizeWhitespace(e);
2039         }
2040
2041         static readonly string[] DocsNodeOrder = {
2042                 "typeparam", "param", "summary", "returns", "value", "remarks",
2043         };
2044
2045         private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2046         {
2047                 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
2048                 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
2049                         for (int j = 0; j < children.Count; ++j) {
2050                                 XmlNode c = children [j];
2051                                 if (c.Name == DocsNodeOrder [i]) {
2052                                         newChildren.Add (c);
2053                                 }
2054                         }
2055                 }
2056                 if (newChildren.Count >= 0)
2057                         docs.PrependChild ((XmlNode) newChildren [0]);
2058                 for (int i = 1; i < newChildren.Count; ++i) {
2059                         XmlNode prev = (XmlNode) newChildren [i-1];
2060                         XmlNode cur  = (XmlNode) newChildren [i];
2061                         docs.RemoveChild (cur);
2062                         docs.InsertAfter (cur, prev);
2063                 }
2064         }
2065         
2066
2067         private static void UpdateParameters (XmlElement e, string element, string[] values)
2068         {       
2069                 if (values != null) {
2070                         XmlNode[] paramnodes = new XmlNode[values.Length];
2071                         
2072                         // Some documentation had param nodes with leading spaces.
2073                         foreach (XmlElement paramnode in e.SelectNodes(element)){
2074                                 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2075                         }
2076                         
2077                         // If a member has only one parameter, we can track changes to
2078                         // the name of the parameter easily.
2079                         if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2080                                 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2081                         }
2082
2083                         bool reinsert = false;
2084
2085                         // Pick out existing and still-valid param nodes, and
2086                         // create nodes for parameters not in the file.
2087                         Hashtable seenParams = new Hashtable();
2088                         for (int pi = 0; pi < values.Length; pi++) {
2089                                 string p = values [pi];
2090                                 seenParams[p] = pi;
2091                                 
2092                                 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2093                                 if (paramnodes[pi] != null) continue;
2094                                 
2095                                 XmlElement pe = e.OwnerDocument.CreateElement(element);
2096                                 pe.SetAttribute("name", p);
2097                                 pe.InnerText = "To be added.";
2098                                 paramnodes[pi] = pe;
2099                                 reinsert = true;
2100                         }
2101
2102                         // Remove parameters that no longer exist and check all params are in the right order.
2103                         int idx = 0;
2104                         MyXmlNodeList todelete = new MyXmlNodeList ();
2105                         foreach (XmlElement paramnode in e.SelectNodes(element)) {
2106                                 string name = paramnode.GetAttribute("name");
2107                                 if (!seenParams.ContainsKey(name)) {
2108                                         if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2109                                                 Error ("The following param node can only be deleted if the --delete option is given: ");
2110                                                 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2111                                                         // delegate type
2112                                                         Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2113                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2114                                                                         name);
2115                                                 }
2116                                                 else {
2117                                                         Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2118                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2119                                                                         e.ParentNode.Attributes ["MemberName"].Value, 
2120                                                                         name);
2121                                                 }
2122                                                 Error ("\tValue={0}", paramnode.OuterXml);
2123                                         } else {
2124                                                 todelete.Add (paramnode);
2125                                         }
2126                                         continue;
2127                                 }
2128
2129                                 if ((int)seenParams[name] != idx)
2130                                         reinsert = true;
2131                                 
2132                                 idx++;
2133                         }
2134
2135                         foreach (XmlNode n in todelete) {
2136                                 n.ParentNode.RemoveChild (n);
2137                         }
2138                         
2139                         // Re-insert the parameter nodes at the top of the doc section.
2140                         if (reinsert)
2141                                 for (int pi = values.Length-1; pi >= 0; pi--)
2142                                         e.PrependChild(paramnodes[pi]);
2143                 } else {
2144                         // Clear all existing param nodes
2145                         foreach (XmlNode paramnode in e.SelectNodes(element)) {
2146                                 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2147                                         Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2148                                         Console.WriteLine(paramnode.OuterXml);
2149                                 } else {
2150                                         paramnode.ParentNode.RemoveChild(paramnode);
2151                                 }
2152                         }
2153                 }
2154         }
2155
2156         private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2157         {
2158                 string existingName = pe.GetAttribute ("name");
2159                 pe.SetAttribute ("name", newName);
2160                 if (existingName == newName)
2161                         return;
2162                 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2163                         if (paramref.GetAttribute ("name").Trim () == existingName)
2164                                 paramref.SetAttribute ("name", newName);
2165         }
2166
2167         private static void NormalizeWhitespace(XmlElement e) {
2168                 // Remove all text and whitespace nodes from the element so it
2169                 // is outputted with nice indentation and no blank lines.
2170                 ArrayList deleteNodes = new ArrayList();
2171                 foreach (XmlNode n in e)
2172                         if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2173                                 deleteNodes.Add(n);
2174                 foreach (XmlNode n in deleteNodes)
2175                                 n.ParentNode.RemoveChild(n);
2176         }
2177         
2178         private static bool UpdateAssemblyVersions(XmlElement root, MemberInfo member, bool add)
2179         {
2180                 Type type = member as Type;
2181                 if (type == null)
2182                         type = member.DeclaringType;
2183                 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion(type.Assembly) }, add);
2184         }
2185         
2186         private static string GetAssemblyVersion(Assembly assembly)
2187         {
2188                 return assembly.GetName().Version.ToString();
2189         }
2190         
2191         private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2192         {
2193                 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2194                 if (e == null) {
2195                         e = root.OwnerDocument.CreateElement("AssemblyInfo");
2196                         root.AppendChild(e);
2197                 }
2198                 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2199                 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2200                         foreach (string sv in assemblyVersions)
2201                                 if (v.InnerText == sv)
2202                                         matches.Add (v);
2203                 }
2204                 // matches.Count > 0 && add: ignore -- already present
2205                 if (matches.Count > 0 && !add) {
2206                         foreach (XmlNode c in matches)
2207                                 e.RemoveChild (c);
2208                 }
2209                 else if (matches.Count == 0 && add) {
2210                         foreach (string sv in assemblyVersions) {
2211                                 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2212                                 c.InnerText = sv;
2213                                 e.AppendChild(c);
2214                         }
2215                 }
2216                 // matches.Count == 0 && !add: ignore -- already not present
2217
2218                 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2219                 SortXmlNodes (e, avs, new VersionComparer ());
2220
2221                 return avs.Count != 0;
2222         }
2223
2224 #if !NET_1_0
2225         private static Type[] IgnorableAttributes = {
2226                 // Security related attributes
2227                 typeof (System.Reflection.AssemblyKeyFileAttribute),
2228                 typeof (System.Reflection.AssemblyDelaySignAttribute),
2229                 // Present in @RefType
2230                 typeof (System.Runtime.InteropServices.OutAttribute),
2231                 // For naming the indexer to use when not using indexers
2232                 typeof (System.Reflection.DefaultMemberAttribute),
2233                 // for decimal constants
2234                 typeof (System.Runtime.CompilerServices.DecimalConstantAttribute),
2235                 // compiler generated code
2236                 typeof (System.Runtime.CompilerServices.CompilerGeneratedAttribute),
2237                 // more compiler generated code, e.g. iterator methods
2238                 typeof (System.Diagnostics.DebuggerHiddenAttribute),
2239                 typeof (System.Runtime.CompilerServices.FixedBufferAttribute),
2240                 typeof (System.Runtime.CompilerServices.UnsafeValueTypeAttribute),
2241                 // extension methods
2242                 typeof (System.Runtime.CompilerServices.ExtensionAttribute),
2243         };
2244 #endif
2245
2246         private static void MakeAttributes(XmlElement root, object attributes, bool assemblyAttributes) {
2247                 int len;
2248 #if NET_1_0
2249                 object[] at = ((ICustomAttributeProvider) attributes).GetCustomAttributes (false);
2250                 len = at.Length;
2251 #else
2252                 System.Collections.Generic.IList<CustomAttributeData> at;
2253                 if (attributes is Assembly)
2254                         at = CustomAttributeData.GetCustomAttributes((Assembly)attributes);
2255                 else if (attributes is MemberInfo)
2256                         at = CustomAttributeData.GetCustomAttributes((MemberInfo)attributes);
2257                 else if (attributes is Module)
2258                         at = CustomAttributeData.GetCustomAttributes((Module)attributes);
2259                 else if (attributes is ParameterInfo)
2260                         at = CustomAttributeData.GetCustomAttributes((ParameterInfo)attributes);
2261                 else
2262                         throw new ArgumentException("unsupported type: " + attributes.GetType().ToString());
2263                 len = at.Count;
2264 #endif
2265         
2266                 if (len == 0) {
2267                         ClearElement(root, "Attributes");
2268                         return;
2269                 }
2270
2271                 bool b = false;
2272                 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2273                 if (e != null)
2274                         e.RemoveAll();
2275                 else
2276                         e = root.OwnerDocument.CreateElement("Attributes");
2277                 
2278 #if !NET_1_0
2279                 foreach (CustomAttributeData a in at) {
2280                         if (!IsPublic (a.Constructor.DeclaringType))
2281                                 continue;
2282                         if (slashdocFormatter.GetName (a.Constructor.DeclaringType) == null)
2283                                 continue;
2284                         
2285                         if (Array.IndexOf (IgnorableAttributes, a.Constructor.DeclaringType) >= 0)
2286                                 continue;
2287                         
2288                         b = true;
2289                         
2290                         StringList fields = new StringList ();
2291
2292                         foreach (CustomAttributeTypedArgument f in a.ConstructorArguments) {
2293                                 fields.Add(MakeAttributesValueString(f.Value));
2294                         }
2295                         foreach (CustomAttributeNamedArgument f in a.NamedArguments) {
2296                                 fields.Add(f.MemberInfo.Name + "=" + MakeAttributesValueString(f.TypedValue.Value));
2297                         }
2298
2299                         string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2300                         if (a2 != "") a2 = "(" + a2 + ")";
2301                         
2302                         XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2303                         e.AppendChild(ae);
2304                         
2305                         string name = a.Constructor.DeclaringType.FullName;
2306                         if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2307                         WriteElementText(ae, "AttributeName", name + a2);
2308                 }
2309 #else
2310                 foreach (Attribute a in at) {
2311                         if (!IsPublic (a.GetType ()))
2312                                 continue;
2313                         if (slashdocFormatter.GetName (a.GetType ()) == null) continue; // hide non-visible attributes
2314                         //if (assemblyAttributes && a.GetType().FullName.StartsWith("System.Reflection.")) continue;
2315                         if (a.GetType().FullName == "System.Reflection.AssemblyKeyFileAttribute" || a.GetType().FullName == "System.Reflection.AssemblyDelaySignAttribute") continue; // hide security-related attributes
2316
2317                         b = true;
2318                         
2319                         // There's no way to reconstruct how the attribute's constructor was called,
2320                         // so as a substitute, just list the value of all of the attribute's public fields.
2321                         
2322                         StringList fields = new StringList ();
2323                         foreach (PropertyInfo f in a.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) {
2324                                 if (f.Name == "TypeId") continue;
2325                                 
2326                                 object v;
2327                                 try {
2328                                         v = f.GetValue(a, null);
2329                                         if (v == null) v = "null";
2330                                         else if (v is string) v = "\"" + v + "\"";
2331                                         else if (v is Type) v = "typeof(" + GetCSharpFullName ((Type)v) + ")";
2332                                         else if (v is Enum) v = v.GetType().FullName + "." + v.ToString().Replace(", ", "|");
2333                                 }
2334                                 catch (Exception ex) {
2335                                         v = "/* error getting property value: " + ex.Message + " */";
2336                                 }
2337                                         
2338                                 fields.Add(f.Name + "=" + v);
2339                         }
2340                         string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2341                         if (a2 != "") a2 = "(" + a2 + ")";
2342                         
2343                         XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2344                         e.AppendChild(ae);
2345                         
2346                         string name = a.GetType().FullName;
2347                         if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2348                         WriteElementText(ae, "AttributeName", name + a2);
2349                 }
2350 #endif
2351                 
2352                 if (b && e.ParentNode == null)
2353                         root.AppendChild(e);
2354                 else if (!b)
2355                         ClearElement(root, "Attributes");
2356                 
2357                 NormalizeWhitespace(e);
2358         }
2359         
2360 #if !NET_1_0
2361         private static string MakeAttributesValueString(object v) {
2362                 if (v == null) return "null";
2363                 else if (v is string) return "\"" + v + "\"";
2364                 else if (v is bool) return (bool)v ? "true" : "false";
2365                 else if (v is Type) return "typeof(" + GetCSharpFullName ((Type)v) + ")";
2366                 else if (v is Enum) {
2367                         string typename = v.GetType ().FullName;
2368                         return typename + "." + v.ToString().Replace(", ", " | " + typename + ".");
2369                 }
2370                 else return v.ToString();
2371         }
2372 #endif
2373         
2374         private static void MakeParameters(XmlElement root, ParameterInfo[] parameters) {
2375                 XmlElement e = WriteElement(root, "Parameters");
2376                 e.RemoveAll();
2377                 foreach (ParameterInfo p in parameters) {
2378                         XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2379                         e.AppendChild(pe);
2380                         pe.SetAttribute("Name", p.Name);
2381                         pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2382                         if (p.ParameterType.IsByRef) {
2383                                 if (p.IsOut) pe.SetAttribute("RefType", "out");
2384                                 else pe.SetAttribute("RefType", "ref");
2385                         }
2386                         MakeAttributes(pe, p, false);
2387                 }
2388         }
2389         
2390         private static void MakeTypeParameters(XmlElement root, Type[] typeParams)
2391         {
2392                 if (typeParams == null || typeParams.Length == 0) {
2393                         XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2394                         if (f != null)
2395                                 root.RemoveChild (f);
2396                         return;
2397                 }
2398                 XmlElement e = WriteElement(root, "TypeParameters");
2399                 e.RemoveAll();
2400                 foreach (Type t in typeParams) {
2401                         XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2402                         e.AppendChild(pe);
2403                         pe.SetAttribute("Name", t.Name);
2404                         MakeAttributes(pe, t, false);
2405 #if !NET_1_0
2406                         XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2407                         GenericParameterAttributes attrs = t.GenericParameterAttributes;
2408                         Type[] constraints = t.GetGenericParameterConstraints ();
2409                         if (attrs == GenericParameterAttributes.None && constraints.Length == 0) {
2410                                 if (ce != null)
2411                                         e.RemoveChild (ce);
2412                                 continue;
2413                         }
2414                         if (ce != null)
2415                                 ce.RemoveAll();
2416                         else {
2417                                 ce = root.OwnerDocument.CreateElement ("Constraints");
2418                         }
2419                         pe.AppendChild (ce);
2420                         if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2421                                 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2422                         if ((attrs & GenericParameterAttributes.Covariant) != 0)
2423                                 AppendElementText (ce, "ParameterAttribute", "Covariant");
2424                         if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2425                                 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2426                         if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2427                                 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2428                         if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2429                                 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2430                         foreach (Type c in constraints) {
2431                                 AppendElementText (ce, 
2432                                                 c.IsInterface ? "InterfaceName" : "BaseTypeName", GetDocTypeFullName (c));
2433                         }
2434 #endif
2435                 }
2436         }
2437
2438         private static void MakeParameters(XmlElement root, MemberInfo mi) {
2439                 if (mi is ConstructorInfo) MakeParameters(root, ((ConstructorInfo)mi).GetParameters());
2440                 else if (mi is MethodInfo) {
2441                         MethodBase mb = (MethodBase) mi;
2442                         ParameterInfo[] parameters = mb.GetParameters();
2443                         MakeParameters(root, parameters);
2444                         if (parameters.Length > 0 && DocUtils.IsExtensionMethod (mb)) {
2445                                 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2446                                 p.SetAttribute ("RefType", "this");
2447                         }
2448                 }
2449                 else if (mi is PropertyInfo) {
2450                         ParameterInfo[] parameters = ((PropertyInfo)mi).GetIndexParameters();
2451                         if (parameters.Length > 0)
2452                                 MakeParameters(root, parameters);
2453                         else
2454                                 return;
2455                 }
2456                 else if (mi is FieldInfo) return;
2457                 else if (mi is EventInfo) return;
2458                 else throw new ArgumentException();
2459         }
2460
2461         private static string GetDocParameterType (Type type)
2462         {
2463                 return GetDocTypeFullName (type).Replace ("@", "&");
2464         }
2465
2466         private static void MakeReturnValue(XmlElement root, Type type, ICustomAttributeProvider attributes) {
2467                 XmlElement e = WriteElement(root, "ReturnValue");
2468                 e.RemoveAll();
2469                 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2470                 if (attributes != null)
2471                         MakeAttributes(e, attributes, false);
2472         }
2473         
2474         private static void MakeReturnValue(XmlElement root, MemberInfo mi) {
2475                 if (mi is ConstructorInfo) return;
2476                 else if (mi is MethodInfo) MakeReturnValue(root, ((MethodInfo)mi).ReturnType, ((MethodInfo)mi).ReturnTypeCustomAttributes);
2477                 else if (mi is PropertyInfo) MakeReturnValue(root, ((PropertyInfo)mi).PropertyType, null);
2478                 else if (mi is FieldInfo) MakeReturnValue(root, ((FieldInfo)mi).FieldType, null);
2479                 else if (mi is EventInfo) MakeReturnValue(root, ((EventInfo)mi).EventHandlerType, null);
2480                 else throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2481         }
2482         
2483         private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2484                 MemberInfo mi = info.Member;
2485                 if (mi is Type) return null;
2486
2487                 string sigs = MakeMemberSignature(mi);
2488                 if (sigs == null) return null; // not publicly visible
2489                 
2490                 // no documentation for property/event accessors.  Is there a better way of doing this?
2491                 if (mi.Name.StartsWith("get_")) return null;
2492                 if (mi.Name.StartsWith("set_")) return null;
2493                 if (mi.Name.StartsWith("add_")) return null;
2494                 if (mi.Name.StartsWith("remove_")) return null;
2495                 if (mi.Name.StartsWith("raise_")) return null;
2496                 
2497                 XmlElement me = doc.CreateElement("Member");
2498                 me.SetAttribute("MemberName", GetMemberName (mi));
2499                 
2500                 info.Node = me;
2501                 UpdateMember(info);
2502
2503                 if (since != null) {
2504                         XmlNode docs = me.SelectSingleNode("Docs");
2505                         docs.AppendChild (CreateSinceNode (doc));
2506                 }
2507                 
2508                 return me;
2509         }
2510
2511         private static string GetMemberName (MemberInfo mi)
2512         {
2513                 MethodBase mb = mi as MethodBase;
2514                 if (mb == null) {
2515                         PropertyInfo pi = mi as PropertyInfo;
2516                         if (pi == null)
2517                                 return mi.Name;
2518                         return DocUtils.GetPropertyName (pi);
2519                 }
2520                 StringBuilder sb = new StringBuilder (mi.Name.Length);
2521                 if (!DocUtils.IsExplicitlyImplemented (mb))
2522                         sb.Append (mi.Name);
2523                 else {
2524                         Type iface;
2525                         MethodInfo ifaceMethod;
2526                         DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2527                         sb.Append (GetDocTypeFullName (iface));
2528                         sb.Append ('.');
2529                         sb.Append (ifaceMethod.Name);
2530                 }
2531                 if (DocUtils.GetContainsGenericParameters (mb)) {
2532                         Type[] typeParams = DocUtils.GetGenericArguments (mb);
2533                         if (typeParams.Length > 0) {
2534                                 sb.Append ("<");
2535                                 sb.Append (typeParams [0].Name);
2536                                 for (int i = 1; i < typeParams.Length; ++i)
2537                                         sb.Append (",").Append (typeParams [i].Name);
2538                                 sb.Append (">");
2539                         }
2540                 }
2541                 return sb.ToString ();
2542         }
2543
2544         private static int CountChars (string s, char c)
2545         {
2546                 int count = 0;
2547                 for (int i = 0; i < s.Length; ++i) {
2548                         if (s [i] == c)
2549                                 ++count;
2550                 }
2551                 return count;
2552         }
2553         
2554         static bool IsDelegate(Type type) {
2555                 return typeof(System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
2556         }
2557         
2558         /// SIGNATURE GENERATION FUNCTIONS
2559         
2560         private static bool InterfaceNotFromAnother(Type i, Type[] i2) {
2561                 foreach (Type t in i2)
2562                         if (i != t && Array.IndexOf(t.GetInterfaces(), i) != -1)
2563                                 return false;
2564                 return true;
2565         }
2566         
2567         static string MakeTypeSignature (Type type) {
2568                 return csharpFormatter.GetDeclaration (type);
2569         }
2570
2571         static string MakeMemberSignature(MemberInfo mi) {
2572                 return csharpFullFormatter.GetDeclaration (mi);
2573         }
2574
2575         static string GetMemberType(MemberInfo mi) {
2576                 if (mi is ConstructorInfo) return "Constructor";
2577                 if (mi is MethodInfo) return "Method";
2578                 if (mi is PropertyInfo) return "Property";
2579                 if (mi is FieldInfo) return "Field";
2580                 if (mi is EventInfo) return "Event";
2581                 throw new ArgumentException();
2582         }
2583
2584         private static string GetDocTypeName (Type type)
2585         {
2586                 return docTypeFormatter.GetName (type);
2587         }
2588
2589         private static string GetDocTypeFullName (Type type)
2590         {
2591                 return DocTypeFullMemberFormatter.Default.GetName (type);
2592         }
2593
2594         private static string GetCSharpFullName (Type type)
2595         {
2596                 return DocTypeFullMemberFormatter.Default.GetName (type);
2597         }
2598
2599         class DocsNodeInfo {
2600                 public DocsNodeInfo (XmlElement node)
2601                 {
2602                         this.Node = node;
2603                 }
2604
2605                 public DocsNodeInfo (XmlElement node, Type type)
2606                         : this (node)
2607                 {
2608                         SetType (type);
2609                 }
2610
2611                 public DocsNodeInfo (XmlElement node, MemberInfo member)
2612                         : this (node)
2613                 {
2614                         SetMemberInfo (member);
2615                 }
2616
2617                 public void SetType (Type type)
2618                 {
2619                         if (type == null)
2620                                 throw new ArgumentNullException ("type");
2621                         GenericParameters = DocUtils.GetGenericArguments (type);
2622                         if (type.DeclaringType != null) {
2623                                 Type[] declGenParams = DocUtils.GetGenericArguments (type.DeclaringType);
2624                                 if (declGenParams != null && GenericParameters.Length == declGenParams.Length) {
2625                                         GenericParameters = null;
2626                                 }
2627                                 else if (declGenParams != null) {
2628                                         Type[] nestedParams = new Type [GenericParameters.Length - declGenParams.Length];
2629                                         for (int i = 0; i < nestedParams.Length; ++i) {
2630                                                 nestedParams [i] = GenericParameters [i+declGenParams.Length];
2631                                         }
2632                                         GenericParameters = nestedParams;
2633                                 }
2634                         }
2635                         if (IsDelegate(type)) {
2636                                 Parameters = type.GetMethod("Invoke").GetParameters();
2637                                 ReturnType = type.GetMethod("Invoke").ReturnType;
2638                         }
2639                         SetSlashDocs (type);
2640                 }
2641
2642                 public void SetMemberInfo (MemberInfo member)
2643                 {
2644                         if (member == null)
2645                                 throw new ArgumentNullException ("member");
2646                         ReturnIsReturn = true;
2647                         AddRemarks = true;
2648                         Member = member;
2649                         
2650                         if (member is MethodInfo || member is ConstructorInfo) {
2651                                 Parameters = ((MethodBase) member).GetParameters ();
2652                                 if (DocUtils.GetContainsGenericParameters ((MethodBase) member)) {
2653                                         GenericParameters = DocUtils.GetGenericArguments ((MethodBase) member);
2654                                 }
2655                         }
2656                         else if (member is PropertyInfo) {
2657                                 Parameters = ((PropertyInfo) member).GetIndexParameters ();
2658                         }
2659                                 
2660                         if (member is MethodInfo) {
2661                                 ReturnType = ((MethodInfo) member).ReturnType;
2662                         } else if (member is PropertyInfo) {
2663                                 ReturnType = ((PropertyInfo) member).PropertyType;
2664                                 ReturnIsReturn = false;
2665                         }
2666
2667                         // no remarks section for enum members
2668                         if (member.DeclaringType != null && member.DeclaringType.IsEnum)
2669                                 AddRemarks = false;
2670                         SetSlashDocs (member);
2671                 }
2672
2673                 private void SetSlashDocs (MemberInfo member)
2674                 {
2675                         if (slashdocs == null)
2676                                 return;
2677
2678                         string slashdocsig = slashdocFormatter.GetDeclaration (member);
2679                         if (slashdocsig != null)
2680                                 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2681                 }
2682
2683                 public Type ReturnType;
2684                 public Type[] GenericParameters;
2685                 public ParameterInfo[] Parameters;
2686                 public bool ReturnIsReturn;
2687                 public XmlElement Node;
2688                 public bool AddRemarks = true;
2689                 public XmlNode SlashDocs;
2690                 public XmlReader EcmaDocs;
2691                 public MemberInfo Member;
2692         }
2693
2694         static string GetXPathForMember (DocumentationMember member)
2695         {
2696                 StringBuilder xpath = new StringBuilder ();
2697                 xpath.Append ("//Members/Member[@MemberName=\"")
2698                         .Append (member.MemberName)
2699                         .Append ("\"]");
2700                 if (member.Parameters != null && member.Parameters.Count > 0) {
2701                         xpath.Append ("/Parameters[count(Parameter) = ")
2702                                 .Append (member.Parameters.Count);
2703                         for (int i = 0; i < member.Parameters.Count; ++i) {
2704                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2705                                 xpath.Append (member.Parameters [i]);
2706                                 xpath.Append ("\"");
2707                         }
2708                         xpath.Append ("]/..");
2709                 }
2710                 return xpath.ToString ();
2711         }
2712
2713         public static string GetXPathForMember (XPathNavigator member)
2714         {
2715                 StringBuilder xpath = new StringBuilder ();
2716                 xpath.Append ("//Type[@FullName=\"")
2717                         .Append (SelectSingleNode (member, "../../@FullName").Value)
2718                         .Append ("\"]/");
2719                 xpath.Append ("Members/Member[@MemberName=\"")
2720                         .Append (SelectSingleNode (member, "@MemberName").Value)
2721                         .Append ("\"]");
2722                 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2723                 if (parameters.Count > 0) {
2724                         xpath.Append ("/Parameters[count(Parameter) = ")
2725                                 .Append (parameters.Count);
2726                         int i = 0;
2727                         while (parameters.MoveNext ()) {
2728                                 ++i;
2729                                 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2730                                 xpath.Append (parameters.Current.Value);
2731                                 xpath.Append ("\"");
2732                         }
2733                         xpath.Append ("]/..");
2734                 }
2735                 return xpath.ToString ();
2736         }
2737
2738         public static string GetXPathForMember (MemberInfo member)
2739         {
2740                 StringBuilder xpath = new StringBuilder ();
2741                 xpath.Append ("//Type[@FullName=\"")
2742                         .Append (member.DeclaringType.FullName)
2743                         .Append ("\"]/");
2744                 xpath.Append ("Members/Member[@MemberName=\"")
2745                         .Append (GetMemberName (member))
2746                         .Append ("\"]");
2747
2748                 ParameterInfo[] parameters = null;
2749                 if (member is MethodBase)
2750                         parameters = ((MethodBase) member).GetParameters ();
2751                 else if (member is PropertyInfo) {
2752                         parameters = ((PropertyInfo) member).GetIndexParameters ();
2753                 }
2754                 if (parameters != null && parameters.Length > 0) {
2755                         xpath.Append ("/Parameters[count(Parameter) = ")
2756                                 .Append (parameters.Length);
2757                         for (int i = 0; i < parameters.Length; ++i) {
2758                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2759                                 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2760                                 xpath.Append ("\"");
2761                         }
2762                         xpath.Append ("]/..");
2763                 }
2764                 return xpath.ToString ();
2765         }
2766 }
2767
2768 static class DocUtils {
2769         public static bool GetContainsGenericParameters (Type type)
2770         {
2771 #if NET_1_0
2772                 return false;
2773 #else
2774                 return type.ContainsGenericParameters;
2775 #endif
2776         }
2777
2778         public static bool GetContainsGenericParameters (MethodBase mb)
2779         {
2780 #if NET_1_0
2781                 return false;
2782 #else
2783                 return mb.ContainsGenericParameters;
2784 #endif
2785         }
2786
2787         public static Type[] GetGenericArguments (Type type)
2788         {
2789 #if NET_1_0
2790                 return new Type [0];
2791 #else
2792                 return type.GetGenericArguments ();
2793 #endif
2794         }
2795
2796         public static Type[] GetGenericArguments (MethodBase mb)
2797         {
2798 #if NET_1_0
2799                 return new Type [0];
2800 #else
2801                 return mb.GetGenericArguments ();
2802 #endif
2803         }
2804
2805         public static Type GetGenericTypeDefinition (Type type)
2806         {
2807 #if NET_1_0
2808                 return null;
2809 #else
2810                 return type.GetGenericTypeDefinition ();
2811 #endif
2812         }
2813
2814         public static Type[] GetGenericParameterConstraints (Type type)
2815         {
2816 #if NET_1_0
2817                 return null;
2818 #else
2819                 return type.GetGenericParameterConstraints ();
2820 #endif
2821         }
2822
2823         public static bool IsGenericType (Type type)
2824         {
2825 #if NET_1_0
2826                 return false;
2827 #else
2828                 return type.IsGenericType;
2829 #endif
2830         }
2831
2832         public static bool IsGenericParameter (Type type)
2833         {
2834 #if NET_1_0
2835                 return false;
2836 #else
2837                 return type.IsGenericParameter;
2838 #endif
2839         }
2840
2841         public static bool IsExplicitlyImplemented (MethodBase method)
2842         {
2843                 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2844         }
2845
2846         public static string GetTypeDotMember (string name)
2847         {
2848                 int startType, startMethod;
2849                 startType = startMethod = -1;
2850                 for (int i = 0; i < name.Length; ++i) {
2851                         if (name [i] == '.') {
2852                                 startType = startMethod;
2853                                 startMethod = i;
2854                         }
2855                 }
2856                 return name.Substring (startType+1);
2857         }
2858
2859         public static string GetMember (string name)
2860         {
2861                 int i = name.LastIndexOf ('.');
2862                 if (i == -1)
2863                         return name;
2864                 return name.Substring (i+1);
2865         }
2866
2867         public static void GetInfoForExplicitlyImplementedMethod (
2868                         MethodBase method, out Type iface, out MethodInfo ifaceMethod)
2869         {
2870                 Type declType = method.DeclaringType;
2871                 foreach (Type declIface in declType.GetInterfaces ()) {
2872                         InterfaceMapping map = declType.GetInterfaceMap (declIface);
2873                         for (int i = 0; i < map.TargetMethods.Length; ++i)
2874                                 if (method == map.TargetMethods [i]) {
2875                                         iface       = map.InterfaceType;
2876                                         ifaceMethod = map.InterfaceMethods [i];
2877                                         return;
2878                                 }
2879                 }
2880                 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2881         }
2882
2883         public static string[] ToStringArray (StringList list)
2884         {
2885 #if NET_1_0
2886                 return (string[]) list.ToArray (typeof(string));
2887 #else
2888                 return list.ToArray ();
2889 #endif
2890         }
2891
2892         public static string GetPropertyName (PropertyInfo pi)
2893         {
2894                 // Issue: (g)mcs-generated assemblies that explicitly implement
2895                 // properties don't specify the full namespace, just the 
2896                 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2897                 MethodInfo method = pi.GetGetMethod (true);
2898                 if (method == null)
2899                         method = pi.GetSetMethod (true);
2900                 if (!IsExplicitlyImplemented (method))
2901                         return pi.Name;
2902
2903                 // Need to determine appropriate namespace for this member.
2904                 Type iface;
2905                 MethodInfo ifaceMethod;
2906                 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2907                 return string.Join (".", new string[]{
2908                                 DocTypeFullMemberFormatter.Default.GetName (iface),
2909                                 GetMember (pi.Name)});
2910         }
2911
2912         public static string PathCombine (string dir, string path)
2913         {
2914                 if (dir == null)
2915                         dir = "";
2916                 if (path == null)
2917                         path = "";
2918                 return Path.Combine (dir, path);
2919         }
2920
2921         public static bool IsExtensionMethod (MethodBase method)
2922         {
2923 #if NET_1_0
2924                 return false;
2925 #else
2926                 return 
2927                         method.GetCustomAttributes (
2928                                         typeof(System.Runtime.CompilerServices.ExtensionAttribute), 
2929                                         false).Length != 0 &&
2930                         method.DeclaringType.GetCustomAttributes (
2931                                         typeof(System.Runtime.CompilerServices.ExtensionAttribute), 
2932                                         false).Length != 0;
2933 #endif
2934         }
2935 }
2936
2937 class DocumentationMember {
2938         public StringToStringMap MemberSignatures = new StringToStringMap ();
2939         public string ReturnType;
2940         public StringList Parameters;
2941         public string MemberName;
2942         public string MemberType;
2943
2944         public DocumentationMember (XmlReader reader)
2945         {
2946                 MemberName = reader.GetAttribute ("MemberName");
2947                 int depth = reader.Depth;
2948                 bool go = true;
2949                 StringList p = new StringList ();
2950                 do {
2951                         if (reader.NodeType != XmlNodeType.Element)
2952                                 continue;
2953                         switch (reader.Name) {
2954                                 case "MemberSignature":
2955                                         MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2956                                         break;
2957                                 case "MemberType":
2958                                         MemberType = reader.ReadElementString ();
2959                                         break;
2960                                 case "ReturnType":
2961                                         if (reader.Depth == depth + 2)
2962                                                 ReturnType = reader.ReadElementString ();
2963                                         break;
2964                                 case "Parameter":
2965                                         if (reader.Depth == depth + 2)
2966                                                 p.Add (reader.GetAttribute ("Type"));
2967                                         break;
2968                                 case "Docs":
2969                                         if (reader.Depth == depth + 1)
2970                                                 go = false;
2971                                         break;
2972                         }
2973                 } while (go && reader.Read () && reader.Depth >= depth);
2974                 if (p.Count > 0) {
2975                         Parameters = p;
2976                 }
2977         }
2978
2979         public DocumentationMember (XmlNode node)
2980         {
2981                 MemberName = node.Attributes ["MemberName"].Value;
2982                 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
2983                         XmlAttribute l = n.Attributes ["Language"];
2984                         XmlAttribute v = n.Attributes ["Value"];
2985                         if (l != null && v != null)
2986                                 MemberSignatures [l.Value] = v.Value;
2987                 }
2988                 MemberType = node.SelectSingleNode ("MemberType").InnerText;
2989                 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
2990                 if (rt != null)
2991                         ReturnType = rt.InnerText;
2992                 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
2993                 if (p.Count > 0) {
2994                         Parameters = new StringList (p.Count);
2995                         for (int i = 0; i < p.Count; ++i)
2996                                 Parameters.Add (p [i].Attributes ["Type"].Value);
2997                 }
2998         }
2999 }
3000
3001 public abstract class MemberFormatter {
3002         public string GetName (MemberInfo member)
3003         {
3004                 Type type = member as Type;
3005                 if (type != null)
3006                         return GetTypeName (type);
3007                 ConstructorInfo ctor = member as ConstructorInfo;
3008                 if (ctor != null)
3009                         return GetConstructorName (ctor);
3010                 MethodInfo method = member as MethodInfo;
3011                 if (method != null)
3012                         return GetMethodName (method);
3013                 PropertyInfo prop = member as PropertyInfo;
3014                 if (prop != null)
3015                         return GetPropertyName (prop);
3016                 FieldInfo field = member as FieldInfo;
3017                 if (field != null)
3018                         return GetFieldName (field);
3019                 EventInfo e = member as EventInfo;
3020                 if (e != null)
3021                         return GetEventName (e);
3022                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3023         }
3024
3025         protected virtual string GetTypeName (Type type)
3026         {
3027                 if (type == null)
3028                         throw new ArgumentNullException ("type");
3029                 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3030         }
3031
3032         protected virtual char[] ArrayDelimeters {
3033                 get {return new char[]{'[', ']'};}
3034         }
3035
3036         protected StringBuilder _AppendTypeName (StringBuilder buf, Type type)
3037         {
3038                 if (type.IsArray) {
3039                         _AppendTypeName (buf, type.GetElementType ()).Append (ArrayDelimeters [0]);
3040                         int rank = type.GetArrayRank ();
3041                         if (rank > 1)
3042                                 buf.Append (new string (',', rank-1));
3043                         return buf.Append (ArrayDelimeters [1]);
3044                 }
3045                 if (type.IsByRef) {
3046                         return AppendRefTypeName (buf, type);
3047                 }
3048                 if (type.IsPointer) {
3049                         return AppendPointerTypeName (buf, type);
3050                 }
3051                 AppendNamespace (buf, type);
3052                 if (DocUtils.IsGenericParameter (type)) {
3053                         return AppendTypeName (buf, type);
3054                 }
3055                 if (!DocUtils.IsGenericType (type)) {
3056                         if (type.DeclaringType != null)
3057                                 AppendTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3058                         return AppendTypeName (buf, type);
3059                 }
3060                 return AppendGenericType (buf, type);
3061         }
3062
3063         protected virtual StringBuilder AppendNamespace (StringBuilder buf, Type type)
3064         {
3065                 if (type.Namespace != null && type.Namespace.Length > 0)
3066                         buf.Append (type.Namespace).Append ('.');
3067                 return buf;
3068         }
3069
3070         protected virtual StringBuilder AppendTypeName (StringBuilder buf, Type type)
3071         {
3072                 return AppendTypeName (buf, type.Name);
3073         }
3074
3075         protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3076         {
3077                 int n = typename.IndexOf ("`");
3078                 if (n >= 0)
3079                         return buf.Append (typename.Substring (0, n));
3080                 return buf.Append (typename);
3081         }
3082
3083         protected virtual string RefTypeModifier {
3084                 get {return "@";}
3085         }
3086
3087         protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, Type type)
3088         {
3089                 return _AppendTypeName (buf, type.GetElementType ()).Append (RefTypeModifier);
3090         }
3091
3092         protected virtual string PointerModifier {
3093                 get {return "*";}
3094         }
3095
3096         protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, Type type)
3097         {
3098                 return _AppendTypeName (buf, type.GetElementType ()).Append (PointerModifier);
3099         }
3100
3101         protected virtual char[] GenericTypeContainer {
3102                 get {return new char[]{'<', '>'};}
3103         }
3104
3105         protected virtual char NestedTypeSeparator {
3106                 get {return '.';}
3107         }
3108
3109         protected virtual StringBuilder AppendGenericType (StringBuilder buf, Type type)
3110         {
3111                 Type[] genArgs = DocUtils.GetGenericArguments (type);
3112                 int genArg = 0;
3113                 if (type.DeclaringType != null) {
3114                         AppendTypeName (buf, type.DeclaringType);
3115                         if (DocUtils.IsGenericType (type.DeclaringType)) {
3116                                 buf.Append (GenericTypeContainer [0]);
3117                                 int max = DocUtils.GetGenericArguments (type.DeclaringType).Length;
3118                                 _AppendTypeName (buf, genArgs [genArg++]);
3119                                 while (genArg < max) {
3120                                         buf.Append (",");
3121                                         _AppendTypeName (buf, genArgs [genArg++]);
3122                                 }
3123                                 buf.Append (GenericTypeContainer [1]);
3124                         }
3125                         buf.Append (NestedTypeSeparator);
3126                 }
3127                 AppendTypeName (buf, type);
3128                 if (genArg < genArgs.Length) {
3129                         buf.Append (GenericTypeContainer [0]);
3130                         _AppendTypeName (buf, genArgs [genArg++]);
3131                         while (genArg < genArgs.Length) {
3132                                 buf.Append (",");
3133                                 _AppendTypeName (buf, genArgs [genArg++]);
3134                         }
3135                         buf.Append (GenericTypeContainer [1]);
3136                 }
3137                 return buf;
3138         }
3139
3140         protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3141         {
3142                 return buf;
3143         }
3144
3145         protected virtual string GetConstructorName (ConstructorInfo constructor)
3146         {
3147                 return constructor.Name;
3148         }
3149
3150         protected virtual string GetMethodName (MethodInfo method)
3151         {
3152                 return method.Name;
3153         }
3154
3155         protected virtual string GetPropertyName (PropertyInfo property)
3156         {
3157                 return property.Name;
3158         }
3159
3160         protected virtual string GetFieldName (FieldInfo field)
3161         {
3162                 return field.Name;
3163         }
3164
3165         protected virtual string GetEventName (EventInfo e)
3166         {
3167                 return e.Name;
3168         }
3169
3170         public string GetDeclaration (MemberInfo member)
3171         {
3172                 Type type = member as Type;
3173                 if (type != null)
3174                         return GetTypeDeclaration (type);
3175                 ConstructorInfo ctor = member as ConstructorInfo;
3176                 if (ctor != null)
3177                         return GetConstructorDeclaration (ctor);
3178                 MethodInfo method = member as MethodInfo;
3179                 if (method != null)
3180                         return GetMethodDeclaration (method);
3181                 PropertyInfo prop = member as PropertyInfo;
3182                 if (prop != null)
3183                         return GetPropertyDeclaration (prop);
3184                 FieldInfo field = member as FieldInfo;
3185                 if (field != null)
3186                         return GetFieldDeclaration (field);
3187                 EventInfo e = member as EventInfo;
3188                 if (e != null)
3189                         return GetEventDeclaration (e);
3190                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3191         }
3192
3193         protected virtual string GetTypeDeclaration (Type type)
3194         {
3195                 if (type == null)
3196                         throw new ArgumentNullException ("type");
3197                 StringBuilder buf = new StringBuilder (type.Name.Length);
3198                 _AppendTypeName (buf, type);
3199                 AppendGenericTypeConstraints (buf, type);
3200                 return buf.ToString ();
3201         }
3202
3203         protected virtual string GetConstructorDeclaration (ConstructorInfo constructor)
3204         {
3205                 return GetConstructorName (constructor);
3206         }
3207
3208         protected virtual string GetMethodDeclaration (MethodInfo method)
3209         {
3210                 // Special signature for destructors.
3211                 if (method.Name == "Finalize" && method.GetParameters().Length == 0)
3212                         return GetFinalizerName (method);
3213
3214                 StringBuilder buf = new StringBuilder ();
3215
3216                 AppendVisibility (buf, method);
3217                 if (buf.Length == 0 && 
3218                                 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3219                         return null;
3220
3221                 AppendModifiers (buf, method);
3222
3223                 if (buf.Length != 0)
3224                         buf.Append (" ");
3225                 buf.Append (GetName (method.ReturnType)).Append (" ");
3226
3227                 AppendMethodName (buf, method);
3228                 AppendGenericMethod (buf, method).Append (" ");
3229                 AppendParameters (buf, method, method.GetParameters ());
3230                 AppendGenericMethodConstraints (buf, method);
3231                 return buf.ToString ();
3232         }
3233
3234         protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3235         {
3236                 return buf.Append (method.Name);
3237         }
3238
3239         protected virtual string GetFinalizerName (MethodInfo method)
3240         {
3241                 return "Finalize";
3242         }
3243
3244         protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3245         {
3246                 return buf;
3247         }
3248
3249         protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3250         {
3251                 return buf;
3252         }
3253
3254         protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3255         {
3256                 return buf;
3257         }
3258
3259         protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3260         {
3261                 return buf;
3262         }
3263
3264         protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3265         {
3266                 return buf;
3267         }
3268
3269         protected virtual string GetPropertyDeclaration (PropertyInfo property)
3270         {
3271                 return GetPropertyName (property);
3272         }
3273
3274         protected virtual string GetFieldDeclaration (FieldInfo field)
3275         {
3276                 return GetFieldName (field);
3277         }
3278
3279         protected virtual string GetEventDeclaration (EventInfo e)
3280         {
3281                 return GetEventName (e);
3282         }
3283 }
3284
3285 class CSharpFullMemberFormatter : MemberFormatter {
3286
3287         protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3288         {
3289                 if (GetCSharpType (type.FullName) == null && type.Namespace != null && type.Namespace.Length > 0 && type.Namespace != "System")
3290                         buf.Append (type.Namespace).Append ('.');
3291                 return buf;
3292         }
3293
3294         private string GetCSharpType (string t)
3295         {
3296                 switch (t) {
3297                 case "System.Byte":    return "byte";
3298                 case "System.SByte":   return "sbyte";
3299                 case "System.Int16":   return "short";
3300                 case "System.Int32":   return "int";
3301                 case "System.Int64":   return "long";
3302
3303                 case "System.UInt16":  return "ushort";
3304                 case "System.UInt32":  return "uint";
3305                 case "System.UInt64":  return "ulong";
3306
3307                 case "System.Single":  return "float";
3308                 case "System.Double":  return "double";
3309                 case "System.Decimal": return "decimal";
3310                 case "System.Boolean": return "bool";
3311                 case "System.Char":    return "char";
3312                 case "System.Void":    return "void";
3313                 case "System.String":  return "string";
3314                 case "System.Object":  return "object";
3315                 }
3316                 return null;
3317         }
3318
3319         protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3320         {
3321                 if (DocUtils.IsGenericParameter (type))
3322                         return buf.Append (type.Name);
3323                 string t = type.FullName;
3324                 if (!t.StartsWith ("System.")) {
3325                         return base.AppendTypeName (buf, type);
3326                 }
3327
3328                 string s = GetCSharpType (t);
3329                 if (s != null)
3330                         return buf.Append (s);
3331                 
3332                 return base.AppendTypeName (buf, type);
3333         }
3334
3335         protected override string GetTypeDeclaration (Type type)
3336         {
3337                 string visibility = GetTypeVisibility (type.Attributes);
3338                 if (visibility == null)
3339                         return null;
3340
3341                 StringBuilder buf = new StringBuilder ();
3342                 
3343                 buf.Append (visibility);
3344                 buf.Append (" ");
3345
3346                 MemberFormatter full = new CSharpFullMemberFormatter ();
3347
3348                 if (IsDelegate(type)) {
3349                         buf.Append("delegate ");
3350                         MethodInfo invoke = type.GetMethod ("Invoke");
3351                         buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
3352                         buf.Append (GetName (type));
3353                         AppendParameters (buf, invoke, invoke.GetParameters ());
3354                         AppendGenericTypeConstraints (buf, type);
3355                         buf.Append (";");
3356
3357                         return buf.ToString();
3358                 }
3359                 
3360                 if (type.IsAbstract && !type.IsInterface)
3361                         buf.Append("abstract ");
3362                 if (type.IsSealed && !IsDelegate(type) && !type.IsValueType)
3363                         buf.Append("sealed ");
3364                 buf.Replace ("abstract sealed", "static");
3365
3366                 buf.Append (GetTypeKind (type));
3367                 buf.Append (" ");
3368                 buf.Append (GetCSharpType (type.FullName) == null 
3369                                 ? GetName (type) 
3370                                 : type.Name);
3371
3372                 if (!type.IsEnum) {
3373                         Type basetype = type.BaseType;
3374                         if (basetype == typeof(object) || type.IsValueType) // don't show this in signatures
3375                                 basetype = null;
3376                         
3377                         ArrayList interface_names = new ArrayList ();
3378                         foreach (Type i in type.GetInterfaces ())
3379                                 if ((type.BaseType == null || Array.IndexOf (type.BaseType.GetInterfaces (), i) == -1) && 
3380                                                 InterfaceNotFromAnother (i, type.GetInterfaces ()))
3381                                         interface_names.Add (full.GetName (i));
3382                         interface_names.Sort ();
3383                         
3384                         if (basetype != null || interface_names.Count > 0)
3385                                 buf.Append (" : ");
3386                         
3387                         if (basetype != null) {
3388                                 buf.Append (full.GetName (basetype));
3389                                 if (interface_names.Count > 0)
3390                                         buf.Append (", ");
3391                         }
3392                         
3393                         for (int i = 0; i < interface_names.Count; i++){
3394                                 if (i != 0)
3395                                         buf.Append (", ");
3396                                 buf.Append (interface_names [i]);
3397                         }
3398                         AppendGenericTypeConstraints (buf, type);
3399                 }
3400
3401                 return buf.ToString ();
3402         }
3403
3404         static string GetTypeKind (Type t)
3405         {
3406                 if (t.IsEnum)
3407                         return "enum";
3408                 if (t.IsClass || t == typeof(System.Enum))
3409                         return "class";
3410                 if (t.IsInterface)
3411                         return "interface";
3412                 if (t.IsValueType)
3413                         return "struct";
3414                 throw new ArgumentException(t.FullName);
3415         }
3416
3417         static string GetTypeVisibility (TypeAttributes ta)
3418         {
3419                 switch (ta & TypeAttributes.VisibilityMask) {
3420                 case TypeAttributes.Public:
3421                 case TypeAttributes.NestedPublic:
3422                         return "public";
3423
3424                 case TypeAttributes.NestedFamily:
3425                 case TypeAttributes.NestedFamORAssem:
3426                         return "protected";
3427
3428                 default:
3429                         return null;
3430                 }
3431         }
3432
3433         static bool IsDelegate(Type type)
3434         {
3435                 return typeof (System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
3436         }
3437         
3438         private static bool InterfaceNotFromAnother(Type i, Type[] i2)
3439         {
3440                 foreach (Type t in i2)
3441                         if (i != t && Array.IndexOf (t.GetInterfaces(), i) != -1)
3442                                 return false;
3443                 return true;
3444         }
3445
3446         protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3447         {
3448                 if (!DocUtils.GetContainsGenericParameters (type))
3449                         return buf;
3450                 return AppendConstraints (buf, DocUtils.GetGenericArguments (type));
3451         }
3452
3453         private StringBuilder AppendConstraints (StringBuilder buf, Type[] genArgs)
3454         {
3455 #if !NET_1_0
3456                 foreach (Type genArg in genArgs) {
3457                         GenericParameterAttributes attrs = genArg.GenericParameterAttributes;
3458                         Type[] constraints = genArg.GetGenericParameterConstraints ();
3459                         if (attrs == GenericParameterAttributes.None && constraints.Length == 0)
3460                                 continue;
3461                         buf.Append (" where ").Append (genArg.Name).Append (" : ");
3462                         bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3463                         bool isvt  = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3464                         bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3465                         bool comma = false;
3466                         if (isref) {
3467                                 buf.Append ("class");
3468                                 comma = true;
3469                         }
3470                         else if (isvt) {
3471                                 buf.Append ("struct");
3472                                 comma = true;
3473                         }
3474                         if (constraints.Length > 0 && !isvt) {
3475                                 if (comma)
3476                                         buf.Append (", ");
3477                                 buf.Append (GetTypeName (constraints [0]));
3478                                 for (int i = 1; i < constraints.Length; ++i)
3479                                         buf.Append (", ").Append (GetTypeName (constraints [i]));
3480                         }
3481                         if (isnew && !isvt) {
3482                                 if (comma)
3483                                         buf.Append (", ");
3484                                 buf.Append ("new()");
3485                         }
3486                 }
3487 #endif
3488                 return buf;
3489         }
3490
3491         protected override string GetConstructorDeclaration (ConstructorInfo constructor)
3492         {
3493                 StringBuilder buf = new StringBuilder ();
3494                 AppendVisibility (buf, constructor);
3495                 if (buf.Length == 0)
3496                         return null;
3497
3498                 buf.Append (' ');
3499                 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3500                 AppendParameters (buf, constructor, constructor.GetParameters ());
3501                 buf.Append (';');
3502
3503                 return buf.ToString ();
3504         }
3505         
3506         protected override string GetMethodDeclaration (MethodInfo method)
3507         {
3508                 string decl = base.GetMethodDeclaration (method);
3509                 if (decl != null)
3510                         return decl + ";";
3511                 return null;
3512         }
3513
3514         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3515         {
3516                 if (DocUtils.IsExplicitlyImplemented (method)) {
3517                         Type iface;
3518                         MethodInfo ifaceMethod;
3519                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3520                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3521                                 .Append ('.')
3522                                 .Append (ifaceMethod.Name);
3523                 }
3524                 return base.AppendMethodName (buf, method);
3525         }
3526
3527         protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3528         {
3529                 if (!DocUtils.GetContainsGenericParameters (method))
3530                         return buf;
3531                 return AppendConstraints (buf, DocUtils.GetGenericArguments (method));
3532         }
3533
3534         protected override string RefTypeModifier {
3535                 get {return "";}
3536         }
3537
3538         protected override string GetFinalizerName (MethodInfo method)
3539         {
3540                 return "~" + method.DeclaringType.Name + " ()"; 
3541         }
3542
3543         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3544         {
3545                 if (method == null)
3546                         return buf;
3547                 if (method.IsPublic)
3548                         return buf.Append ("public");
3549                 if (method.IsFamily || method.IsFamilyOrAssembly)
3550                         return buf.Append ("protected");
3551                 return buf;
3552         }
3553
3554         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3555         {
3556                 string modifiers = String.Empty;
3557                 if (method.IsStatic) modifiers += " static";
3558                 if (method.IsVirtual && !method.IsAbstract) {
3559                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3560                         else modifiers += " override";
3561                 }
3562                 if (method.IsAbstract && !method.DeclaringType.IsInterface) modifiers += " abstract";
3563                 if (method.IsFinal) modifiers += " sealed";
3564                 if (modifiers == " virtual sealed") modifiers = "";
3565
3566                 return buf.Append (modifiers);
3567         }
3568
3569         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3570         {
3571                 if (DocUtils.GetContainsGenericParameters (method)) {
3572                         Type[] args = DocUtils.GetGenericArguments (method);
3573                         if (args.Length > 0) {
3574                                 buf.Append ("<");
3575                                 buf.Append (args [0].Name);
3576                                 for (int i = 1; i < args.Length; ++i)
3577                                         buf.Append (",").Append (args [i].Name);
3578                                 buf.Append (">");
3579                         }
3580                 }
3581                 return buf;
3582         }
3583
3584         protected override StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3585         {
3586                 return AppendParameters (buf, method, parameters, '(', ')');
3587         }
3588
3589         private StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters, char begin, char end)
3590         {
3591                 buf.Append (begin);
3592
3593                 if (parameters.Length > 0) {
3594                         if (DocUtils.IsExtensionMethod (method))
3595                                 buf.Append ("this ");
3596                         AppendParameter (buf, parameters [0]);
3597                         for (int i = 1; i < parameters.Length; ++i) {
3598                                 buf.Append (", ");
3599                                 AppendParameter (buf, parameters [i]);
3600                         }
3601                 }
3602
3603                 return buf.Append (end);
3604         }
3605
3606         private StringBuilder AppendParameter (StringBuilder buf, ParameterInfo parameter)
3607         {
3608                 if (parameter.ParameterType.IsByRef) {
3609                         if (parameter.IsOut)
3610                                 buf.Append ("out ");
3611                         else
3612                                 buf.Append ("ref ");
3613                 }
3614                 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3615                 return buf.Append (parameter.Name);
3616         }
3617
3618         protected override string GetPropertyDeclaration (PropertyInfo property)
3619         {
3620                 MethodInfo method;
3621
3622                 string get_visible = null;
3623                 if ((method = property.GetGetMethod (true)) != null && 
3624                                 (DocUtils.IsExplicitlyImplemented (method) || 
3625                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3626                         get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3627                 string set_visible = null;
3628                 if ((method = property.GetSetMethod (true)) != null &&
3629                                 (DocUtils.IsExplicitlyImplemented (method) || 
3630                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3631                         set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3632
3633                 if ((set_visible == null) && (get_visible == null))
3634                         return null;
3635
3636                 string visibility;
3637                 StringBuilder buf = new StringBuilder ();
3638                 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3639                         buf.Append (visibility = get_visible);
3640                 else if (set_visible != null && get_visible == null)
3641                         buf.Append (visibility = set_visible);
3642                 else
3643                         buf.Append (visibility = "public");
3644
3645                 // Pick an accessor to use for static/virtual/override/etc. checks.
3646                 method = property.GetSetMethod (true);
3647                 if (method == null)
3648                         method = property.GetGetMethod (true);
3649         
3650                 string modifiers = String.Empty;
3651                 if (method.IsStatic) modifiers += " static";
3652                 if (method.IsVirtual && !method.IsAbstract) {
3653                                 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3654                                         modifiers += " virtual";
3655                                 else
3656                                         modifiers += " override";
3657                 }
3658                 if (method.IsAbstract && !method.DeclaringType.IsInterface)
3659                         modifiers += " abstract";
3660                 if (method.IsFinal)
3661                         modifiers += " sealed";
3662                 if (modifiers == " virtual sealed")
3663                         modifiers = "";
3664                 buf.Append (modifiers).Append (' ');
3665
3666                 buf.Append (GetName (property.PropertyType)).Append (' ');
3667         
3668                 MemberInfo[] defs = property.DeclaringType.GetDefaultMembers ();
3669                 string name = property.Name;
3670                 foreach (MemberInfo mi in defs) {
3671                         if (mi == property) {
3672                                 name = "this";
3673                                 break;
3674                         }
3675                 }
3676                 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3677         
3678                 if (property.GetIndexParameters ().Length != 0) {
3679                         AppendParameters (buf, method, property.GetIndexParameters (), '[', ']');
3680                 }
3681
3682                 buf.Append (" {");
3683                 if (set_visible != null) {
3684                         if (set_visible != visibility)
3685                                 buf.Append (' ').Append (set_visible);
3686                         buf.Append (" set;");
3687                 }
3688                 if (get_visible != null) {
3689                         if (get_visible != visibility)
3690                                 buf.Append (' ').Append (get_visible);
3691                         buf.Append (" get;");
3692                 }
3693                 buf.Append (" }");
3694         
3695                 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3696         }
3697
3698         protected override string GetFieldDeclaration (FieldInfo field)
3699         {
3700                 if (field.DeclaringType.IsEnum && field.Name == "value__")
3701                         return null; // This member of enums aren't documented.
3702
3703                 StringBuilder buf = new StringBuilder ();
3704                 AppendFieldVisibility (buf, field);
3705                 if (buf.Length == 0)
3706                         return null;
3707
3708                 if (field.DeclaringType.IsEnum)
3709                         return field.Name;
3710
3711                 if (field.IsStatic && !field.IsLiteral)
3712                         buf.Append (" static");
3713                 if (field.IsInitOnly)
3714                         buf.Append (" readonly");
3715                 if (field.IsLiteral)
3716                         buf.Append (" const");
3717
3718                 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3719                 buf.Append (field.Name);
3720                 AppendFieldValue (buf, field);
3721                 buf.Append (';');
3722
3723                 return buf.ToString ();
3724         }
3725
3726         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldInfo field)
3727         {
3728                 if (field.IsPublic)
3729                         return buf.Append ("public");
3730                 if (field.IsFamily || field.IsFamilyOrAssembly)
3731                         return buf.Append ("protected");
3732                 return buf;
3733         }
3734
3735         static StringBuilder AppendFieldValue (StringBuilder buf, FieldInfo field)
3736         {
3737                 // enums have a value__ field, which we ignore, and FieldInfo.GetValue()
3738                 // on a GenericType results in InvalidOperationException
3739                 if (field.DeclaringType.IsEnum || 
3740                                 DocUtils.IsGenericType (field.DeclaringType))
3741                         return buf;
3742                 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
3743                         object val = null;
3744                         try {
3745                                 val   = field.GetValue (null);
3746                         } catch {
3747                                 return buf;
3748                         }
3749                         if (val == null)
3750                                 buf.Append (" = ").Append ("null");
3751                         else if (val is Enum)
3752                                 buf.Append (" = ").Append (val.ToString ());
3753                         else if (val is IFormattable) {
3754                                 string value = ((IFormattable)val).ToString();
3755                                 if (val is string)
3756                                         value = "\"" + value + "\"";
3757                                 buf.Append (" = ").Append (value);
3758                         }
3759                 }
3760                 return buf;
3761         }
3762
3763         protected override string GetEventDeclaration (EventInfo e)
3764         {
3765                 StringBuilder buf = new StringBuilder ();
3766                 if (AppendVisibility (buf, e.GetAddMethod (true)).Length == 0) {
3767                         return null;
3768                 }
3769
3770                 AppendModifiers (buf, e.GetAddMethod (true));
3771
3772                 buf.Append (" event ");
3773                 buf.Append (GetName (e.EventHandlerType)).Append (' ');
3774                 buf.Append (e.Name).Append (';');
3775
3776                 return buf.ToString ();
3777         }
3778 }
3779
3780 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3781         protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3782         {
3783                 return buf;
3784         }
3785 }
3786
3787 class DocTypeFullMemberFormatter : MemberFormatter {
3788         public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3789
3790         protected override char NestedTypeSeparator {
3791                 get {return '+';}
3792         }
3793 }
3794
3795 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3796         protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3797         {
3798                 return buf;
3799         }
3800 }
3801
3802 class SlashDocMemberFormatter : MemberFormatter {
3803
3804         protected override char[] GenericTypeContainer {
3805                 get {return new char[]{'{', '}'};}
3806         }
3807
3808         private bool AddTypeCount = true;
3809
3810         protected override string GetTypeName (Type type)
3811         {
3812                 return base.GetTypeName (type);
3813         }
3814
3815         private Type genDeclType;
3816         private MethodBase genDeclMethod;
3817
3818         protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3819         {
3820                 if (DocUtils.IsGenericParameter (type)) {
3821                         int l = buf.Length;
3822                         if (genDeclType != null) {
3823                                 Type[] genArgs = DocUtils.GetGenericArguments (genDeclType);
3824                                 for (int i = 0; i < genArgs.Length; ++i) {
3825                                         if (genArgs [i].Name == type.Name) {
3826                                                 buf.Append ('`').Append (i);
3827                                                 break;
3828                                         }
3829                                 }
3830                         }
3831                         if (genDeclMethod != null) {
3832                                 Type[] genArgs = null;
3833                                 if (DocUtils.GetContainsGenericParameters (genDeclMethod)) {
3834                                         genArgs = DocUtils.GetGenericArguments (genDeclMethod);
3835                                 }
3836                                 else
3837                                         genArgs = new Type[0];
3838                                 for (int i = 0; i < genArgs.Length; ++i) {
3839                                         if (genArgs [i].Name == type.Name) {
3840                                                 buf.Append ("``").Append (i);
3841                                                 break;
3842                                         }
3843                                 }
3844                         }
3845                         if (genDeclType == null && genDeclMethod == null) {
3846                                 // Probably from within an explicitly implemented interface member,
3847                                 // where CSC uses parameter names instead of indices (why?), e.g.
3848                                 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3849                                 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3850                                 buf.Append (type.Name);
3851                         }
3852                         if (buf.Length == l) {
3853                                 throw new Exception (string.Format (
3854                                                 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}", 
3855                                                 type.Name, genDeclType, genDeclMethod));
3856                         }
3857                 }
3858                 else {
3859                         base.AppendTypeName (buf, type);
3860                         if (AddTypeCount) {
3861                                 int numArgs = DocUtils.GetGenericArguments (type).Length;
3862                                 if (type.DeclaringType != null)
3863                                         numArgs -= DocUtils.GetGenericArguments (type).Length;
3864                                 if (numArgs > 0) {
3865                                         buf.Append ('`').Append (numArgs);
3866                                 }
3867                         }
3868                 }
3869                 return buf;
3870         }
3871
3872         protected override StringBuilder AppendGenericType (StringBuilder buf, Type type)
3873         {
3874                 if (!AddTypeCount)
3875                         base.AppendGenericType (buf, type);
3876                 else
3877                         AppendType (buf, type);
3878                 return buf;
3879         }
3880
3881         private StringBuilder AppendType (StringBuilder buf, Type type)
3882         {
3883                 int numArgs = DocUtils.GetGenericArguments (type).Length;
3884                 if (type.DeclaringType != null) {
3885                         AppendType (buf, type.DeclaringType).Append (NestedTypeSeparator);
3886                         numArgs -= DocUtils.GetGenericArguments (type.DeclaringType).Length;
3887                 }
3888                 base.AppendTypeName (buf, type);
3889                 if (numArgs > 0) {
3890                         buf.Append ('`').Append (numArgs);
3891                 }
3892                 return buf;
3893         }
3894
3895         protected override string GetConstructorName (ConstructorInfo constructor)
3896         {
3897                 return GetMethodBaseName (constructor, "#ctor");
3898         }
3899
3900         protected override string GetMethodName (MethodInfo method)
3901         {
3902                 string name = null;
3903                 if (!DocUtils.IsExplicitlyImplemented (method))
3904                         name = method.Name;
3905                 else {
3906                         Type iface;
3907                         MethodInfo ifaceMethod;
3908                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3909                         AddTypeCount = false;
3910                         name = GetTypeName (iface) + "." + ifaceMethod.Name;
3911                         AddTypeCount = true;
3912                 }
3913                 return GetMethodBaseName (method, name);
3914         }
3915
3916         private string GetMethodBaseName (MethodBase method, string name)
3917         {
3918                 StringBuilder buf = new StringBuilder ();
3919                 buf.Append (GetTypeName (method.DeclaringType));
3920                 buf.Append ('.');
3921                 buf.Append (name.Replace (".", "#"));
3922                 if (DocUtils.GetContainsGenericParameters (method)) {
3923                         Type[] genArgs = DocUtils.GetGenericArguments (method);
3924                         if (genArgs.Length > 0)
3925                                 buf.Append ("``").Append (genArgs.Length);
3926                 }
3927                 ParameterInfo[] parameters = method.GetParameters ();
3928                 genDeclType = method.DeclaringType;
3929                 genDeclMethod = method;
3930                 AppendParameters (buf, DocUtils.GetGenericArguments (method.DeclaringType), parameters);
3931                 genDeclType = null;
3932                 genDeclMethod = null;
3933                 return buf.ToString ();
3934         }
3935
3936         private StringBuilder AppendParameters (StringBuilder buf, Type[] genArgs, ParameterInfo[] parameters)
3937         {
3938                 if (parameters.Length == 0)
3939                         return buf;
3940
3941                 buf.Append ('(');
3942
3943                 AppendParameter (buf, genArgs, parameters [0]);
3944                 for (int i = 1; i < parameters.Length; ++i) {
3945                         buf.Append (',');
3946                         AppendParameter (buf, genArgs, parameters [i]);
3947                 }
3948
3949                 return buf.Append (')');
3950         }
3951
3952         private StringBuilder AppendParameter (StringBuilder buf, Type[] genArgs, ParameterInfo parameter)
3953         {
3954                 AddTypeCount = false;
3955                 buf.Append (GetTypeName (parameter.ParameterType));
3956                 AddTypeCount = true;
3957                 return buf;
3958         }
3959
3960         protected override string GetPropertyName (PropertyInfo property)
3961         {
3962                 string name = null;
3963
3964                 MethodInfo method = property.GetGetMethod (true);
3965                 if (method == null)
3966                         method = property.GetSetMethod (true);
3967                 if (!DocUtils.IsExplicitlyImplemented (method))
3968                         name = property.Name;
3969                 else {
3970                         Type iface;
3971                         MethodInfo ifaceMethod;
3972                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3973                         AddTypeCount = false;
3974                         name = string.Join ("#", new string[]{
3975                                         GetTypeName (iface).Replace (".", "#"),
3976                                         DocUtils.GetMember (property.Name)
3977                         });
3978                         AddTypeCount = true;
3979                 }
3980
3981                 StringBuilder buf = new StringBuilder ();
3982                 buf.Append (GetName (property.DeclaringType));
3983                 buf.Append ('.');
3984                 buf.Append (name);
3985                 ParameterInfo[] parameters = property.GetIndexParameters ();
3986                 if (parameters.Length > 0) {
3987                         genDeclType = property.DeclaringType;
3988                         buf.Append ('(');
3989                         Type[] genArgs = DocUtils.GetGenericArguments (property.DeclaringType);
3990                         AppendParameter (buf, genArgs, parameters [0]);
3991                         for (int i = 1; i < parameters.Length; ++i) {
3992                                  buf.Append (',');
3993                                  AppendParameter (buf, genArgs, parameters [i]);
3994                         }
3995                         buf.Append (')');
3996                         genDeclType = null;
3997                 }
3998                 return buf.ToString ();
3999         }
4000
4001         protected override string GetFieldName (FieldInfo field)
4002         {
4003                 return string.Format ("{0}.{1}",
4004                         GetName (field.DeclaringType), field.Name);
4005         }
4006
4007         protected override string GetEventName (EventInfo e)
4008         {
4009                 return string.Format ("{0}.{1}",
4010                         GetName (e.DeclaringType), e.Name);
4011         }
4012
4013         protected override string GetTypeDeclaration (Type type)
4014         {
4015                 string name = GetName (type);
4016                 if (type == null)
4017                         return null;
4018                 return "T:" + name;
4019         }
4020
4021         protected override string GetConstructorDeclaration (ConstructorInfo constructor)
4022         {
4023                 string name = GetName (constructor);
4024                 if (name == null)
4025                         return null;
4026                 return "M:" + name;
4027         }
4028
4029         protected override string GetMethodDeclaration (MethodInfo method)
4030         {
4031                 string name = GetName (method);
4032                 if (name == null)
4033                         return null;
4034                 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4035                         genDeclType = method.DeclaringType;
4036                         genDeclMethod = method;
4037                         name += "~" + GetName (method.ReturnType);
4038                         genDeclType = null;
4039                         genDeclMethod = null;
4040                 }
4041                 return "M:" + name;
4042         }
4043
4044         protected override string GetPropertyDeclaration (PropertyInfo property)
4045         {
4046                 string name = GetName (property);
4047                 if (name == null)
4048                         return null;
4049                 return "P:" + name;
4050         }
4051
4052         protected override string GetFieldDeclaration (FieldInfo field)
4053         {
4054                 string name = GetName (field);
4055                 if (name == null)
4056                         return null;
4057                 return "F:" + name;
4058         }
4059
4060         protected override string GetEventDeclaration (EventInfo e)
4061         {
4062                 string name = GetName (e);
4063                 if (name == null)
4064                         return null;
4065                 return "E:" + name;
4066         }
4067 }
4068
4069 class FileNameMemberFormatter : SlashDocMemberFormatter {
4070         protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
4071         {
4072                 return buf;
4073         }
4074
4075         protected override char NestedTypeSeparator {
4076                 get {return '+';}
4077         }
4078 }
4079
4080 }