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