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