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