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