cccb63f5a472b7974b2bd9c95ee9480788395fa4
[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                 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1817                         string cref = slashdocFormatter.GetDeclaration (source.Exception);
1818                         var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1819                         if (node != null)
1820                                 continue;
1821                         XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1822                         e.SetAttribute ("cref", cref);
1823                         e.InnerXml = "To be added; from: <see cref=\"" + 
1824                                 string.Join ("\" />, <see cref=\"", 
1825                                                 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1826                                                 .ToArray ()) +
1827                                 "\" />";
1828                         docs.AppendChild (e);
1829                 }
1830                 SortXmlNodes (docs, docs.SelectNodes ("exception"), 
1831                                 new CrefComparer ());
1832         }
1833
1834         private static void NormalizeWhitespace(XmlElement e) {
1835                 // Remove all text and whitespace nodes from the element so it
1836                 // is outputted with nice indentation and no blank lines.
1837                 ArrayList deleteNodes = new ArrayList();
1838                 foreach (XmlNode n in e)
1839                         if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1840                                 deleteNodes.Add(n);
1841                 foreach (XmlNode n in deleteNodes)
1842                                 n.ParentNode.RemoveChild(n);
1843         }
1844         
1845         private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1846         {
1847                 TypeDefinition type = member as TypeDefinition;
1848                 if (type == null)
1849                         type = member.DeclaringType as TypeDefinition;
1850                 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1851         }
1852         
1853         private static string GetAssemblyVersion (AssemblyDefinition assembly)
1854         {
1855                 return assembly.Name.Version.ToString();
1856         }
1857         
1858         private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1859         {
1860                 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1861                 if (e == null) {
1862                         e = root.OwnerDocument.CreateElement("AssemblyInfo");
1863                         root.AppendChild(e);
1864                 }
1865                 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1866                         .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1867                         .ToList ();
1868                 // matches.Count > 0 && add: ignore -- already present
1869                 if (matches.Count > 0 && !add) {
1870                         foreach (XmlNode c in matches)
1871                                 e.RemoveChild (c);
1872                 }
1873                 else if (matches.Count == 0 && add) {
1874                         foreach (string sv in assemblyVersions) {
1875                                 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1876                                 c.InnerText = sv;
1877                                 e.AppendChild(c);
1878                         }
1879                 }
1880                 // matches.Count == 0 && !add: ignore -- already not present
1881
1882                 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1883                 SortXmlNodes (e, avs, new VersionComparer ());
1884
1885                 return avs.Count != 0;
1886         }
1887
1888         // FIXME: get TypeReferences instead of string comparison?
1889         private static string[] IgnorableAttributes = {
1890                 // Security related attributes
1891                 "System.Reflection.AssemblyKeyFileAttribute",
1892                 "System.Reflection.AssemblyDelaySignAttribute",
1893                 // Present in @RefType
1894                 "System.Runtime.InteropServices.OutAttribute",
1895                 // For naming the indexer to use when not using indexers
1896                 "System.Reflection.DefaultMemberAttribute",
1897                 // for decimal constants
1898                 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1899                 // compiler generated code
1900                 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1901                 // more compiler generated code, e.g. iterator methods
1902                 "System.Diagnostics.DebuggerHiddenAttribute",
1903                 "System.Runtime.CompilerServices.FixedBufferAttribute",
1904                 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1905                 // extension methods
1906                 "System.Runtime.CompilerServices.ExtensionAttribute",
1907                 // Used to differentiate 'object' from C#4 'dynamic'
1908                 "System.Runtime.CompilerServices.DynamicAttribute",
1909         };
1910
1911         private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1912         {
1913                 if (!attributes.Any ()) {
1914                         ClearElement (root, "Attributes");
1915                         return;
1916                 }
1917
1918                 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1919                 if (e != null)
1920                         e.RemoveAll();
1921                 else if (e == null)
1922                         e = root.OwnerDocument.CreateElement("Attributes");
1923                 
1924                 foreach (string attribute in attributes) {
1925                         XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1926                         e.AppendChild(ae);
1927                         
1928                         WriteElementText(ae, "AttributeName", attribute);
1929                 }
1930                 
1931                 if (e.ParentNode == null)
1932                         root.AppendChild(e);
1933
1934                 NormalizeWhitespace(e);
1935         }
1936
1937         public static string MakeAttributesValueString (object v, TypeReference valueType)
1938         {
1939                 if (v == null)
1940                         return "null";
1941                 if (valueType.FullName == "System.Type")
1942                         return "typeof(" + v.ToString () + ")";
1943                 if (valueType.FullName == "System.String")
1944                         return "\"" + v.ToString () + "\"";
1945                 if (valueType.FullName == "System.Char")
1946                         return "'" + v.ToString () + "'";
1947                 if (v is Boolean)
1948                         return (bool)v ? "true" : "false";
1949                 TypeDefinition valueDef = valueType.Resolve ();
1950                 if (valueDef == null || !valueDef.IsEnum)
1951                         return v.ToString ();
1952                 string typename = GetDocTypeFullName (valueType);
1953                 var values = GetEnumerationValues (valueDef);
1954                 long c = ToInt64 (v);
1955                 if (values.ContainsKey (c))
1956                         return typename + "." + values [c];
1957                 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1958                         return string.Join (" | ",
1959                                         (from i in values.Keys
1960                                          where (c & i) != 0
1961                                          select typename + "." + values [i])
1962                                         .DefaultIfEmpty (v.ToString ()).ToArray ());
1963                 }
1964                 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1965         }
1966
1967         private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1968         {
1969                 var values = new Dictionary<long, string> ();
1970                 foreach (var f in 
1971                                 (from f in type.Fields
1972                                  where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1973                                  select f)) {
1974                         values [ToInt64 (f.Constant)] = f.Name;
1975                 }
1976                 return values;
1977         }
1978
1979         static long ToInt64 (object value)
1980         {
1981                 if (value is ulong)
1982                         return (long) (ulong) value;
1983                 return Convert.ToInt64 (value);
1984         }
1985         
1986         private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1987         {
1988                 XmlElement e = WriteElement(root, "Parameters");
1989                 e.RemoveAll();
1990                 foreach (ParameterDefinition p in parameters) {
1991                         XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1992                         e.AppendChild(pe);
1993                         pe.SetAttribute("Name", p.Name);
1994                         pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1995                         if (p.ParameterType is ByReferenceType) {
1996                                 if (p.IsOut) pe.SetAttribute("RefType", "out");
1997                                 else pe.SetAttribute("RefType", "ref");
1998                         }
1999                         MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2000                 }
2001         }
2002         
2003         private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
2004         {
2005                 if (typeParams == null || typeParams.Count == 0) {
2006                         XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2007                         if (f != null)
2008                                 root.RemoveChild (f);
2009                         return;
2010                 }
2011                 XmlElement e = WriteElement(root, "TypeParameters");
2012                 e.RemoveAll();
2013                 foreach (GenericParameter t in typeParams) {
2014                         XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2015                         e.AppendChild(pe);
2016                         pe.SetAttribute("Name", t.Name);
2017                         MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2018                         XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2019                         IList<TypeReference> constraints = t.Constraints;
2020                         GenericParameterAttributes attrs = t.Attributes;
2021                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2022                                 if (ce != null)
2023                                         e.RemoveChild (ce);
2024                                 continue;
2025                         }
2026                         if (ce != null)
2027                                 ce.RemoveAll();
2028                         else {
2029                                 ce = root.OwnerDocument.CreateElement ("Constraints");
2030                         }
2031                         pe.AppendChild (ce);
2032                         if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2033                                 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2034                         if ((attrs & GenericParameterAttributes.Covariant) != 0)
2035                                 AppendElementText (ce, "ParameterAttribute", "Covariant");
2036                         if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2037                                 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2038                         if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2039                                 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2040                         if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2041                                 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2042                         foreach (TypeReference c in constraints) {
2043                                 TypeDefinition cd = c.Resolve ();
2044                                 AppendElementText (ce,
2045                                                 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2046                                                 GetDocTypeFullName (c));
2047                         }
2048                 }
2049         }
2050
2051         private void MakeParameters (XmlElement root, MemberReference mi)
2052         {
2053                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2054                         MakeParameters (root, ((MethodDefinition)mi).Parameters);
2055                 else if (mi is MethodDefinition) {
2056                         MethodDefinition mb = (MethodDefinition) mi;
2057                         IList<ParameterDefinition> parameters = mb.Parameters;
2058                         MakeParameters(root, parameters);
2059                         if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2060                                 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2061                                 p.SetAttribute ("RefType", "this");
2062                         }
2063                 }
2064                 else if (mi is PropertyDefinition) {
2065                         IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2066                         if (parameters.Count > 0)
2067                                 MakeParameters(root, parameters);
2068                         else
2069                                 return;
2070                 }
2071                 else if (mi is FieldDefinition) return;
2072                 else if (mi is EventDefinition) return;
2073                 else throw new ArgumentException();
2074         }
2075
2076         internal static string GetDocParameterType (TypeReference type)
2077         {
2078                 return GetDocTypeFullName (type).Replace ("@", "&");
2079         }
2080
2081         private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes) 
2082         {
2083                 XmlElement e = WriteElement(root, "ReturnValue");
2084                 e.RemoveAll();
2085                 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2086                 if (attributes != null)
2087                         MakeAttributes(e, GetCustomAttributes (attributes, ""));
2088         }
2089         
2090         private void MakeReturnValue (XmlElement root, MemberReference mi)
2091         {
2092                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2093                         return;
2094                 else if (mi is MethodDefinition)
2095                         MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2096                 else if (mi is PropertyDefinition)
2097                         MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2098                 else if (mi is FieldDefinition)
2099                         MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2100                 else if (mi is EventDefinition)
2101                         MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2102                 else
2103                         throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2104         }
2105         
2106         private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2107         {
2108                 MemberReference mi = info.Member;
2109                 if (mi is TypeDefinition) return null;
2110
2111                 string sigs = memberFormatters [0].GetDeclaration (mi);
2112                 if (sigs == null) return null; // not publicly visible
2113                 
2114                 // no documentation for property/event accessors.  Is there a better way of doing this?
2115                 if (mi.Name.StartsWith("get_")) return null;
2116                 if (mi.Name.StartsWith("set_")) return null;
2117                 if (mi.Name.StartsWith("add_")) return null;
2118                 if (mi.Name.StartsWith("remove_")) return null;
2119                 if (mi.Name.StartsWith("raise_")) return null;
2120                 
2121                 XmlElement me = doc.CreateElement("Member");
2122                 me.SetAttribute("MemberName", GetMemberName (mi));
2123                 
2124                 info.Node = me;
2125                 UpdateMember(info);
2126                 if (exceptions.HasValue && 
2127                                 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2128                         UpdateExceptions (info.Node, info.Member);
2129
2130                 if (since != null) {
2131                         XmlNode docs = me.SelectSingleNode("Docs");
2132                         docs.AppendChild (CreateSinceNode (doc));
2133                 }
2134                 
2135                 return me;
2136         }
2137
2138         internal static string GetMemberName (MemberReference mi)
2139         {
2140                 MethodDefinition mb = mi as MethodDefinition;
2141                 if (mb == null) {
2142                         PropertyDefinition pi = mi as PropertyDefinition;
2143                         if (pi == null)
2144                                 return mi.Name;
2145                         return DocUtils.GetPropertyName (pi);
2146                 }
2147                 StringBuilder sb = new StringBuilder (mi.Name.Length);
2148                 if (!DocUtils.IsExplicitlyImplemented (mb))
2149                         sb.Append (mi.Name);
2150                 else {
2151                         TypeReference iface;
2152                         MethodReference ifaceMethod;
2153                         DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2154                         sb.Append (GetDocTypeFullName (iface));
2155                         sb.Append ('.');
2156                         sb.Append (ifaceMethod.Name);
2157                 }
2158                 if (mb.IsGenericMethod ()) {
2159                         IList<GenericParameter> typeParams = mb.GenericParameters;
2160                         if (typeParams.Count > 0) {
2161                                 sb.Append ("<");
2162                                 sb.Append (typeParams [0].Name);
2163                                 for (int i = 1; i < typeParams.Count; ++i)
2164                                         sb.Append (",").Append (typeParams [i].Name);
2165                                 sb.Append (">");
2166                         }
2167                 }
2168                 return sb.ToString ();
2169         }
2170         
2171         /// SIGNATURE GENERATION FUNCTIONS
2172         internal static bool IsPrivate (MemberReference mi)
2173         {
2174                 return memberFormatters [0].GetDeclaration (mi) == null;
2175         }
2176
2177         internal static string GetMemberType (MemberReference mi)
2178         {
2179                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2180                         return "Constructor";
2181                 if (mi is MethodDefinition)
2182                         return "Method";
2183                 if (mi is PropertyDefinition)
2184                         return "Property";
2185                 if (mi is FieldDefinition)
2186                         return "Field";
2187                 if (mi is EventDefinition)
2188                         return "Event";
2189                 throw new ArgumentException();
2190         }
2191
2192         private static string GetDocTypeName (TypeReference type)
2193         {
2194                 return docTypeFormatter.GetName (type);
2195         }
2196
2197         internal static string GetDocTypeFullName (TypeReference type)
2198         {
2199                 return DocTypeFullMemberFormatter.Default.GetName (type);
2200         }
2201
2202         internal static string GetXPathForMember (DocumentationMember member)
2203         {
2204                 StringBuilder xpath = new StringBuilder ();
2205                 xpath.Append ("//Members/Member[@MemberName=\"")
2206                         .Append (member.MemberName)
2207                         .Append ("\"]");
2208                 if (member.Parameters != null && member.Parameters.Count > 0) {
2209                         xpath.Append ("/Parameters[count(Parameter) = ")
2210                                 .Append (member.Parameters.Count);
2211                         for (int i = 0; i < member.Parameters.Count; ++i) {
2212                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2213                                 xpath.Append (member.Parameters [i]);
2214                                 xpath.Append ("\"");
2215                         }
2216                         xpath.Append ("]/..");
2217                 }
2218                 return xpath.ToString ();
2219         }
2220
2221         public static string GetXPathForMember (XPathNavigator member)
2222         {
2223                 StringBuilder xpath = new StringBuilder ();
2224                 xpath.Append ("//Type[@FullName=\"")
2225                         .Append (member.SelectSingleNode ("../../@FullName").Value)
2226                         .Append ("\"]/");
2227                 xpath.Append ("Members/Member[@MemberName=\"")
2228                         .Append (member.SelectSingleNode ("@MemberName").Value)
2229                         .Append ("\"]");
2230                 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2231                 if (parameters.Count > 0) {
2232                         xpath.Append ("/Parameters[count(Parameter) = ")
2233                                 .Append (parameters.Count);
2234                         int i = 0;
2235                         while (parameters.MoveNext ()) {
2236                                 ++i;
2237                                 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2238                                 xpath.Append (parameters.Current.Value);
2239                                 xpath.Append ("\"");
2240                         }
2241                         xpath.Append ("]/..");
2242                 }
2243                 return xpath.ToString ();
2244         }
2245
2246         public static string GetXPathForMember (MemberReference member)
2247         {
2248                 StringBuilder xpath = new StringBuilder ();
2249                 xpath.Append ("//Type[@FullName=\"")
2250                         .Append (member.DeclaringType.FullName)
2251                         .Append ("\"]/");
2252                 xpath.Append ("Members/Member[@MemberName=\"")
2253                         .Append (GetMemberName (member))
2254                         .Append ("\"]");
2255
2256                 IList<ParameterDefinition> parameters = null;
2257                 if (member is MethodDefinition)
2258                         parameters = ((MethodDefinition) member).Parameters;
2259                 else if (member is PropertyDefinition) {
2260                         parameters = ((PropertyDefinition) member).Parameters;
2261                 }
2262                 if (parameters != null && parameters.Count > 0) {
2263                         xpath.Append ("/Parameters[count(Parameter) = ")
2264                                 .Append (parameters.Count);
2265                         for (int i = 0; i < parameters.Count; ++i) {
2266                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2267                                 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2268                                 xpath.Append ("\"");
2269                         }
2270                         xpath.Append ("]/..");
2271                 }
2272                 return xpath.ToString ();
2273         }
2274 }
2275
2276 static class CecilExtensions {
2277         public static string GetDeclaringType(this CustomAttribute attribute)
2278         {
2279                 return attribute.Constructor.DeclaringType.FullName;
2280         }
2281
2282         public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2283         {
2284                 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2285                         yield return (MemberReference) c;
2286                 foreach (var e in type.Events)
2287                         yield return (MemberReference) e;
2288                 foreach (var f in type.Fields)
2289                         yield return (MemberReference) f;
2290                 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2291                         yield return (MemberReference) m;
2292                 foreach (var t in type.NestedTypes)
2293                         yield return (MemberReference) t;
2294                 foreach (var p in type.Properties)
2295                         yield return (MemberReference) p;
2296         }
2297
2298         public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2299         {
2300                 return GetMembers (type).Where (m => m.Name == member);
2301         }
2302
2303         public static MemberReference GetMember (this TypeDefinition type, string member)
2304         {
2305                 return GetMembers (type, member).EnsureZeroOrOne ();
2306         }
2307
2308         static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2309         {
2310                 if (source.Count () > 1)
2311                         throw new InvalidOperationException ("too many matches");
2312                 return source.FirstOrDefault ();
2313         }
2314
2315         public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2316         {
2317                 return type.Methods
2318                         .Where (m => m.Name == method)
2319                         .EnsureZeroOrOne ();
2320         }
2321
2322         public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2323         {
2324                 TypeDefinition def = type as TypeDefinition;
2325                 if (def == null)
2326                         return new MemberReference [0];
2327                 CustomAttribute defMemberAttr = def.CustomAttributes
2328                                 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2329                 if (defMemberAttr == null)
2330                         return new MemberReference [0];
2331                 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2332                 return def.Properties
2333                                 .Where (p => p.Name == name)
2334                                 .Select (p => (MemberReference) p);
2335         }
2336
2337         public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2338         {
2339                 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2340         }
2341
2342         public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2343         {
2344                 return GetTypes (assembly)
2345                         .Where (td => td.FullName == type)
2346                         .EnsureZeroOrOne ();
2347         }
2348
2349         public static bool IsGenericType (this TypeReference type)
2350         {
2351                 return type.GenericParameters.Count > 0;
2352         }
2353
2354         public static bool IsGenericMethod (this MethodReference method)
2355         {
2356                 return method.GenericParameters.Count > 0;
2357         }
2358
2359         public static MemberReference Resolve (this MemberReference member)
2360         {
2361                 FieldReference fr = member as FieldReference;
2362                 if (fr != null)
2363                         return fr.Resolve ();
2364                 MethodReference mr = member as MethodReference;
2365                 if (mr != null)
2366                         return mr.Resolve ();
2367                 TypeReference tr = member as TypeReference;
2368                 if (tr != null)
2369                         return tr.Resolve ();
2370                 PropertyReference pr = member as PropertyReference;
2371                 if (pr != null)
2372                         return pr;
2373                 EventReference er = member as EventReference;
2374                 if (er != null)
2375                         return er;
2376                 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2377         }
2378
2379         public static TypeReference GetUnderlyingType (this TypeDefinition type)
2380         {
2381                 if (!type.IsEnum)
2382                         return type;
2383                 return type.Fields.First (f => f.Name == "value__").FieldType;
2384         }
2385
2386         public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2387         {
2388                 return self.Types.SelectMany (t => t.GetAllTypes ());
2389         }
2390
2391         static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2392         {
2393                 yield return self;
2394
2395                 if (!self.HasNestedTypes)
2396                         yield break;
2397
2398                 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2399                         yield return type;
2400         }
2401 }
2402
2403 static class DocUtils {
2404         public static bool IsExplicitlyImplemented (MethodDefinition method)
2405         {
2406                 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2407         }
2408
2409         public static string GetTypeDotMember (string name)
2410         {
2411                 int startType, startMethod;
2412                 startType = startMethod = -1;
2413                 for (int i = 0; i < name.Length; ++i) {
2414                         if (name [i] == '.') {
2415                                 startType = startMethod;
2416                                 startMethod = i;
2417                         }
2418                 }
2419                 return name.Substring (startType+1);
2420         }
2421
2422         public static string GetMember (string name)
2423         {
2424                 int i = name.LastIndexOf ('.');
2425                 if (i == -1)
2426                         return name;
2427                 return name.Substring (i+1);
2428         }
2429
2430         public static void GetInfoForExplicitlyImplementedMethod (
2431                         MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2432         {
2433                 iface = null;
2434                 ifaceMethod = null;
2435                 if (method.Overrides.Count != 1)
2436                         throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2437                 iface = method.Overrides [0].DeclaringType;
2438                 ifaceMethod = method.Overrides [0];
2439         }
2440
2441         public static string GetPropertyName (PropertyDefinition pi)
2442         {
2443                 // Issue: (g)mcs-generated assemblies that explicitly implement
2444                 // properties don't specify the full namespace, just the 
2445                 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2446                 MethodDefinition method = pi.GetMethod;
2447                 if (method == null)
2448                         method = pi.SetMethod;
2449                 if (!IsExplicitlyImplemented (method))
2450                         return pi.Name;
2451
2452                 // Need to determine appropriate namespace for this member.
2453                 TypeReference iface;
2454                 MethodReference ifaceMethod;
2455                 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2456                 return string.Join (".", new string[]{
2457                                 DocTypeFullMemberFormatter.Default.GetName (iface),
2458                                 GetMember (pi.Name)});
2459         }
2460
2461         public static string GetNamespace (TypeReference type)
2462         {
2463                 if (type.GetElementType ().IsNested)
2464                         type = type.GetElementType ();
2465                 while (type != null && type.IsNested)
2466                         type = type.DeclaringType;
2467                 if (type == null)
2468                         return string.Empty;
2469                 return type.Namespace;
2470         }
2471
2472         public static string PathCombine (string dir, string path)
2473         {
2474                 if (dir == null)
2475                         dir = "";
2476                 if (path == null)
2477                         path = "";
2478                 return Path.Combine (dir, path);
2479         }
2480
2481         public static bool IsExtensionMethod (MethodDefinition method)
2482         {
2483                 return
2484                         method.CustomAttributes
2485                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2486                         && method.DeclaringType.CustomAttributes
2487                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2488         }
2489
2490         public static bool IsDelegate (TypeDefinition type)
2491         {
2492                 TypeReference baseRef = type.BaseType;
2493                 if (baseRef == null)
2494                         return false;
2495                 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2496                                 baseRef.FullName == "System.MulticastDelegate";
2497         }
2498
2499         public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2500         {
2501                 List<TypeReference> decls = new List<TypeReference> ();
2502                 decls.Add (type);
2503                 while (type.DeclaringType != null) {
2504                         decls.Add (type.DeclaringType);
2505                         type = type.DeclaringType;
2506                 }
2507                 decls.Reverse ();
2508                 return decls;
2509         }
2510
2511         public static int GetGenericArgumentCount (TypeReference type)
2512         {
2513                 GenericInstanceType inst = type as GenericInstanceType;
2514                 return inst != null
2515                                 ? inst.GenericArguments.Count
2516                                 : type.GenericParameters.Count;
2517         }
2518
2519         public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2520         {
2521                 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2522                 List<TypeReference> userInterfaces = new List<TypeReference> ();
2523                 foreach (TypeReference iface in type.Interfaces) {
2524                         TypeReference lookup = iface.Resolve () ?? iface;
2525                         if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2526                                 userInterfaces.Add (iface);
2527                 }
2528                 return userInterfaces;
2529         }
2530
2531         private static string GetQualifiedTypeName (TypeReference type)
2532         {
2533                 return "[" + type.Scope.Name + "]" + type.FullName;
2534         }
2535
2536         private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2537         {
2538                 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2539                 Action<TypeDefinition> a = null;
2540                 a = t => {
2541                         if (t == null) return;
2542                         foreach (TypeReference r in t.Interfaces) {
2543                                 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2544                                 a (r.Resolve ());
2545                         }
2546                 };
2547                 TypeReference baseRef = type.BaseType;
2548                 while (baseRef != null) {
2549                         TypeDefinition baseDef = baseRef.Resolve ();
2550                         if (baseDef != null) {
2551                                 a (baseDef);
2552                                 baseRef = baseDef.BaseType;
2553                         }
2554                         else
2555                                 baseRef = null;
2556                 }
2557                 foreach (TypeReference r in type.Interfaces)
2558                         a (r.Resolve ());
2559                 return inheritedInterfaces;
2560         }
2561 }
2562
2563 class DocsNodeInfo {
2564         public DocsNodeInfo (XmlElement node)
2565         {
2566                 this.Node = node;
2567         }
2568
2569         public DocsNodeInfo (XmlElement node, TypeDefinition type)
2570                 : this (node)
2571         {
2572                 SetType (type);
2573         }
2574
2575         public DocsNodeInfo (XmlElement node, MemberReference member)
2576                 : this (node)
2577         {
2578                 SetMemberInfo (member);
2579         }
2580
2581         void SetType (TypeDefinition type)
2582         {
2583                 if (type == null)
2584                         throw new ArgumentNullException ("type");
2585                 Type = type;
2586                 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2587                 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2588                 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2589                 for (int i = 0; i < declTypes.Count - 1; ++i) {
2590                         int remove = System.Math.Min (maxGenArgs, 
2591                                         DocUtils.GetGenericArgumentCount (declTypes [i]));
2592                         maxGenArgs -= remove;
2593                         while (remove-- > 0)
2594                                 GenericParameters.RemoveAt (0);
2595                 }
2596                 if (DocUtils.IsDelegate (type)) {
2597                         Parameters = type.GetMethod("Invoke").Parameters;
2598                         ReturnType = type.GetMethod("Invoke").ReturnType;
2599                         ReturnIsReturn = true;
2600                 }
2601         }
2602
2603         void SetMemberInfo (MemberReference member)
2604         {
2605                 if (member == null)
2606                         throw new ArgumentNullException ("member");
2607                 ReturnIsReturn = true;
2608                 AddRemarks = true;
2609                 Member = member;
2610                 
2611                 if (member is MethodReference ) {
2612                         MethodReference mr = (MethodReference) member;
2613                         Parameters = mr.Parameters;
2614                         if (mr.IsGenericMethod ()) {
2615                                 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2616                         }
2617                 }
2618                 else if (member is PropertyDefinition) {
2619                         Parameters = ((PropertyDefinition) member).Parameters;
2620                 }
2621                         
2622                 if (member is MethodDefinition) {
2623                         ReturnType = ((MethodDefinition) member).ReturnType;
2624                 } else if (member is PropertyDefinition) {
2625                         ReturnType = ((PropertyDefinition) member).PropertyType;
2626                         ReturnIsReturn = false;
2627                 }
2628
2629                 // no remarks section for enum members
2630                 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2631                         AddRemarks = false;
2632         }
2633
2634         public TypeReference ReturnType;
2635         public List<GenericParameter> GenericParameters;
2636         public IList<ParameterDefinition> Parameters;
2637         public bool ReturnIsReturn;
2638         public XmlElement Node;
2639         public bool AddRemarks = true;
2640         public MemberReference Member;
2641         public TypeDefinition Type;
2642 }
2643
2644 class DocumentationEnumerator {
2645
2646         public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2647         {
2648                 return GetDocumentationTypes (assembly, forTypes, null);
2649         }
2650
2651         protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2652         {
2653                 foreach (TypeDefinition type in assembly.GetTypes()) {
2654                         if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2655                                 continue;
2656                         if (seen != null && seen.Contains (type.FullName))
2657                                 continue;
2658                         yield return type;
2659                         foreach (TypeDefinition nested in type.NestedTypes)
2660                                 yield return nested;
2661                 }
2662         }
2663
2664         public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2665         {
2666                 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2667                         if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2668                                 oldmember.RemoveAttribute ("__monodocer-seen__");
2669                                 continue;
2670                         }
2671                         MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2672                         if (m == null) {
2673                                 yield return new DocsNodeInfo (oldmember);
2674                         }
2675                         else {
2676                                 yield return new DocsNodeInfo (oldmember, m);
2677                         }
2678                 }
2679         }
2680
2681         protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2682         {
2683                 string membertype = member.MemberType;
2684                 
2685                 string returntype = member.ReturnType;
2686                 
2687                 string docName = member.MemberName;
2688                 string[] docTypeParams = GetTypeParameters (docName);
2689
2690                 // Loop through all members in this type with the same name
2691                 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2692                         if (mi is TypeDefinition) continue;
2693                         if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2694
2695                         if (MDocUpdater.IsPrivate (mi))
2696                                 continue;
2697
2698                         IList<ParameterDefinition> pis = null;
2699                         string[] typeParams = null;
2700                         if (mi is MethodDefinition) {
2701                                 MethodDefinition mb = (MethodDefinition) mi;
2702                                 pis = mb.Parameters;
2703                                 if (docTypeParams != null && mb.IsGenericMethod ()) {
2704                                         IList<GenericParameter> args = mb.GenericParameters;
2705                                         if (args.Count == docTypeParams.Length) {
2706                                                 typeParams = args.Select (p => p.Name).ToArray ();
2707                                         }
2708                                 }
2709                         }
2710                         else if (mi is PropertyDefinition)
2711                                 pis = ((PropertyDefinition)mi).Parameters;
2712                         
2713                         int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2714                         int pcount = pis == null ? 0 : pis.Count;
2715                         if (mcount != pcount)
2716                                 continue;
2717
2718                         MethodDefinition mDef = mi as MethodDefinition;
2719                         if (mDef != null && !mDef.IsConstructor) {
2720                                 // Casting operators can overload based on return type.
2721                                 if (returntype != GetReplacedString (
2722                                                         MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType), 
2723                                                         typeParams, docTypeParams)) {
2724                                         continue;
2725                                 }
2726                         }
2727
2728                         if (pcount == 0)
2729                                 return mi;
2730                         bool good = true;
2731                         for (int i = 0; i < pis.Count; i++) {
2732                                 string paramType = GetReplacedString (
2733                                         MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2734                                         typeParams, docTypeParams);
2735                                 if (paramType != (string) member.Parameters [i]) {
2736                                         good = false;
2737                                         break;
2738                                 }
2739                         }
2740                         if (!good) continue;
2741
2742                         return mi;
2743                 }
2744                 
2745                 return null;
2746         }
2747
2748         static string[] GetTypeParameters (string docName)
2749         {
2750                 if (docName [docName.Length-1] != '>')
2751                         return null;
2752                 StringList types = new StringList ();
2753                 int endToken = docName.Length-2;
2754                 int i = docName.Length-2;
2755                 do {
2756                         if (docName [i] == ',' || docName [i] == '<') {
2757                                 types.Add (docName.Substring (i + 1, endToken - i));
2758                                 endToken = i-1;
2759                         }
2760                         if (docName [i] == '<')
2761                                 break;
2762                 } while (--i >= 0);
2763
2764                 types.Reverse ();
2765                 return types.ToArray ();
2766         }
2767
2768         protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2769         {
2770                 // need to worry about 4 forms of //@MemberName values:
2771                 //  1. "Normal" (non-generic) member names: GetEnumerator
2772                 //    - Lookup as-is.
2773                 //  2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2774                 //    - try as-is, and try type.member (due to "kludge" for property
2775                 //      support.
2776                 //  3. "Normal" Generic member names: Sort<T> (CSC)
2777                 //    - need to remove generic parameters --> "Sort"
2778                 //  4. Explicitly-implemented interface members for generic interfaces: 
2779                 //    -- System.Collections.Generic.IEnumerable<T>.Current
2780                 //    - Try as-is, and try type.member, *keeping* the generic parameters.
2781                 //     --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2782                 //  5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2783                 //    'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2784                 //    this as (1) or (2).
2785                 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2786                         // Cases 1 & 2
2787                         foreach (MemberReference mi in type.GetMembers (docName))
2788                                 yield return mi;
2789                         if (CountChars (docName, '.') > 0)
2790                                 // might be a property; try only type.member instead of
2791                                 // namespace.type.member.
2792                                 foreach (MemberReference mi in 
2793                                                 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2794                                         yield return mi;
2795                         yield break;
2796                 }
2797                 // cases 3 & 4
2798                 int numLt = 0;
2799                 int numDot = 0;
2800                 int startLt, startType, startMethod;
2801                 startLt = startType = startMethod = -1;
2802                 for (int i = 0; i < docName.Length; ++i) {
2803                         switch (docName [i]) {
2804                                 case '<':
2805                                         if (numLt == 0) {
2806                                                 startLt = i;
2807                                         }
2808                                         ++numLt;
2809                                         break;
2810                                 case '>':
2811                                         --numLt;
2812                                         if (numLt == 0 && (i + 1) < docName.Length)
2813                                                 // there's another character in docName, so this <...> sequence is
2814                                                 // probably part of a generic type -- case 4.
2815                                                 startLt = -1;
2816                                         break;
2817                                 case '.':
2818                                         startType = startMethod;
2819                                         startMethod = i;
2820                                         ++numDot;
2821                                         break;
2822                         }
2823                 }
2824                 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2825                 // case 3
2826                 foreach (MemberReference mi in type.GetMembers (refName))
2827                         yield return mi;
2828
2829                 // case 4
2830                 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2831                         yield return mi;
2832
2833                 // If we _still_ haven't found it, we've hit another generic naming issue:
2834                 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2835                 // explicitly-implemented METHOD names (not properties), e.g. 
2836                 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2837                 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2838                 // which the XML docs will contain.
2839                 //
2840                 // Alas, we can't derive the Mono name from docName, so we need to iterate
2841                 // over all member names, convert them into CSC format, and compare... :-(
2842                 if (numDot == 0)
2843                         yield break;
2844                 foreach (MemberReference mi in type.GetMembers ()) {
2845                         if (MDocUpdater.GetMemberName (mi) == docName)
2846                                 yield return mi;
2847                 }
2848         }
2849
2850         static string GetReplacedString (string typeName, string[] from, string[] to)
2851         {
2852                 if (from == null)
2853                         return typeName;
2854                 for (int i = 0; i < from.Length; ++i)
2855                         typeName = typeName.Replace (from [i], to [i]);
2856                 return typeName;
2857         }
2858
2859         private static int CountChars (string s, char c)
2860         {
2861                 int count = 0;
2862                 for (int i = 0; i < s.Length; ++i) {
2863                         if (s [i] == c)
2864                                 ++count;
2865                 }
2866                 return count;
2867         }
2868 }
2869
2870 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2871
2872         XmlReader ecmadocs;
2873         MDocUpdater app;
2874
2875         public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2876         {
2877                 this.app      = app;
2878                 this.ecmadocs = ecmaDocs;
2879         }
2880
2881         public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2882         {
2883                 HashSet<string> seen = new HashSet<string> ();
2884                 return GetDocumentationTypes (assembly, forTypes, seen)
2885                         .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2886         }
2887
2888         new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2889         {
2890                 int typeDepth = -1;
2891                 while (ecmadocs.Read ()) {
2892                         switch (ecmadocs.Name) {
2893                                 case "Type": {
2894                                         if (typeDepth == -1)
2895                                                 typeDepth = ecmadocs.Depth;
2896                                         if (ecmadocs.NodeType != XmlNodeType.Element)
2897                                                 continue;
2898                                         if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2899                                                 continue;
2900                                         string typename = ecmadocs.GetAttribute ("FullName");
2901                                         string typename2 = MDocUpdater.GetTypeFileName (typename);
2902                                         if (forTypes != null && 
2903                                                         forTypes.BinarySearch (typename) < 0 &&
2904                                                         typename != typename2 &&
2905                                                         forTypes.BinarySearch (typename2) < 0)
2906                                                 continue;
2907                                         TypeDefinition t;
2908                                         if ((t = assembly.GetType (typename)) == null && 
2909                                                         (t = assembly.GetType (typename2)) == null)
2910                                                 continue;
2911                                         seen.Add (typename);
2912                                         if (typename != typename2)
2913                                                 seen.Add (typename2);
2914                                         Console.WriteLine ("  Import: {0}", t.FullName);
2915                                         if (ecmadocs.Name != "Docs") {
2916                                                 int depth = ecmadocs.Depth;
2917                                                 while (ecmadocs.Read ()) {
2918                                                         if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2919                                                                 break;
2920                                                 }
2921                                         }
2922                                         if (!ecmadocs.IsStartElement ("Docs"))
2923                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2924                                         yield return t;
2925                                         break;
2926                                 }
2927                                 default:
2928                                         break;
2929                         }
2930                 }
2931         }
2932
2933         public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2934         {
2935                 return GetMembers (basefile, type)
2936                         .Concat (base.GetDocumentationMembers (basefile, type));
2937         }
2938
2939         private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2940         {
2941                 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2942                         // do nothing
2943                 }
2944                 if (ecmadocs.IsEmptyElement)
2945                         yield break;
2946
2947                 int membersDepth = ecmadocs.Depth;
2948                 bool go = true;
2949                 while (go && ecmadocs.Read ()) {
2950                         switch (ecmadocs.Name) {
2951                                 case "Member": {
2952                                         if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2953                                                 continue;
2954                                         DocumentationMember dm = new DocumentationMember (ecmadocs);
2955                                         string xp = MDocUpdater.GetXPathForMember (dm);
2956                                         XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2957                                         MemberReference m;
2958                                         if (oldmember == null) {
2959                                                 m = GetMember (type, dm);
2960                                                 if (m == null) {
2961                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2962                                                                         type.FullName, dm.MemberSignatures ["C#"]);
2963                                                                         // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2964                                                         continue;
2965                                                 }
2966                                                 // oldmember lookup may have failed due to type parameter renames.
2967                                                 // Try again.
2968                                                 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2969                                                 if (oldmember == null) {
2970                                                         XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2971                                                         oldmember = basefile.CreateElement ("Member");
2972                                                         oldmember.SetAttribute ("MemberName", dm.MemberName);
2973                                                         members.AppendChild (oldmember);
2974                                                         foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2975                                                                 XmlElement ms = basefile.CreateElement ("MemberSignature");
2976                                                                 ms.SetAttribute ("Language", key);
2977                                                                 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2978                                                                 oldmember.AppendChild (ms);
2979                                                         }
2980                                                         oldmember.SetAttribute ("__monodocer-seen__", "true");
2981                                                         Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2982                                                         app.additions++;
2983                                                 }
2984                                         }
2985                                         else {
2986                                                 m = GetMember (type, new DocumentationMember (oldmember));
2987                                                 if (m == null) {
2988                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2989                                                                         type.FullName, dm.MemberSignatures ["C#"]);
2990                                                         continue;
2991                                                 }
2992                                                 oldmember.SetAttribute ("__monodocer-seen__", "true");
2993                                         }
2994                                         DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2995                                         if (ecmadocs.Name != "Docs")
2996                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2997                                         yield return node;
2998                                         break;
2999                                 }
3000                                 case "Members":
3001                                         if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3002                                                 go = false;
3003                                         }
3004                                         break;
3005                         }
3006                 }
3007         }
3008 }
3009
3010 abstract class DocumentationImporter {
3011
3012         public abstract void ImportDocumentation (DocsNodeInfo info);
3013 }
3014
3015 class MsxdocDocumentationImporter : DocumentationImporter {
3016
3017         XmlDocument slashdocs;
3018
3019         public MsxdocDocumentationImporter (string file)
3020         {
3021                 var xml = File.ReadAllText (file);
3022
3023                 // Ensure Unix line endings
3024                 xml = xml.Replace ("\r", "");
3025
3026                 slashdocs = new XmlDocument();
3027                 slashdocs.LoadXml (xml);
3028         }
3029
3030         public override void ImportDocumentation (DocsNodeInfo info)
3031         {
3032                 XmlNode elem = GetDocs (info.Member ?? info.Type);
3033
3034                 if (elem == null)
3035                         return;
3036
3037                 XmlElement e = info.Node;
3038
3039                 if (elem.SelectSingleNode("summary") != null)
3040                         MDocUpdater.ClearElement(e, "summary");
3041                 if (elem.SelectSingleNode("remarks") != null)
3042                         MDocUpdater.ClearElement(e, "remarks");
3043                 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3044                         MDocUpdater.ClearElement(e, "value");
3045                         MDocUpdater.ClearElement(e, "returns");
3046                 }
3047
3048                 foreach (XmlNode child in elem.ChildNodes) {
3049                         switch (child.Name) {
3050                                 case "param":
3051                                 case "typeparam": {
3052                                         XmlAttribute name = child.Attributes ["name"];
3053                                         if (name == null)
3054                                                 break;
3055                                         XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3056                                         if (p2 != null)
3057                                                 p2.InnerXml = child.InnerXml;
3058                                         break;
3059                                 }
3060                                 // Occasionally XML documentation will use <returns/> on
3061                                 // properties, so let's try to normalize things.
3062                                 case "value":
3063                                 case "returns": {
3064                                         XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3065                                         v.InnerXml = child.InnerXml;
3066                                         e.AppendChild (v);
3067                                         break;
3068                                 }
3069                                 case "altmember":
3070                                 case "exception":
3071                                 case "permission": {
3072                                         XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3073                                         if (cref == null)
3074                                                 break;
3075                                         XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3076                                         if (a == null) {
3077                                                 a = e.OwnerDocument.CreateElement (child.Name);
3078                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3079                                                 e.AppendChild (a);
3080                                         }
3081                                         a.InnerXml = child.InnerXml;
3082                                         break;
3083                                 }
3084                                 case "seealso": {
3085                                         XmlAttribute cref = child.Attributes ["cref"];
3086                                         if (cref == null)
3087                                                 break;
3088                                         XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3089                                         if (a == null) {
3090                                                 a = e.OwnerDocument.CreateElement ("altmember");
3091                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3092                                                 e.AppendChild (a);
3093                                         }
3094                                         break;
3095                                 }
3096                                 default: {
3097                                         bool add = true;
3098                                         if (child.NodeType == XmlNodeType.Element && 
3099                                                         e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3100                                                 add = false;
3101                                         if (add)
3102                                                 MDocUpdater.CopyNode (child, e);
3103                                         break;
3104                                 }
3105                         }
3106                 }
3107         }
3108
3109         private XmlNode GetDocs (MemberReference member)
3110         {
3111                 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3112                 if (slashdocsig != null)
3113                         return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3114                 return null;
3115         }
3116 }
3117
3118 class EcmaDocumentationImporter : DocumentationImporter {
3119
3120         XmlReader ecmadocs;
3121
3122         public EcmaDocumentationImporter (XmlReader ecmaDocs)
3123         {
3124                 this.ecmadocs = ecmaDocs;
3125         }
3126
3127         public override void ImportDocumentation (DocsNodeInfo info)
3128         {
3129                 if (!ecmadocs.IsStartElement ("Docs")) {
3130                         return;
3131                 }
3132
3133                 XmlElement e = info.Node;
3134
3135                 int depth = ecmadocs.Depth;
3136                 ecmadocs.ReadStartElement ("Docs");
3137                 while (ecmadocs.Read ()) {
3138                         if (ecmadocs.Name == "Docs") {
3139                                 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3140                                         break;
3141                                 else
3142                                         throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3143                         }
3144                         if (!ecmadocs.IsStartElement ())
3145                                 continue;
3146                         switch (ecmadocs.Name) {
3147                                 case "param":
3148                                 case "typeparam": {
3149                                         string name = ecmadocs.GetAttribute ("name");
3150                                         if (name == null)
3151                                                 break;
3152                                         XmlNode doc = e.SelectSingleNode (
3153                                                         ecmadocs.Name + "[@name='" + name + "']");
3154                                         string value = ecmadocs.ReadInnerXml ();
3155                                         if (doc != null)
3156                                                 doc.InnerXml = value.Replace ("\r", "");
3157                                         break;
3158                                 }
3159                                 case "altmember":
3160                                 case "exception":
3161                                 case "permission":
3162                                 case "seealso": {
3163                                         string name = ecmadocs.Name;
3164                                         string cref = ecmadocs.GetAttribute ("cref");
3165                                         if (cref == null)
3166                                                 break;
3167                                         XmlNode doc = e.SelectSingleNode (
3168                                                         ecmadocs.Name + "[@cref='" + cref + "']");
3169                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3170                                         if (doc != null)
3171                                                 doc.InnerXml = value;
3172                                         else {
3173                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3174                                                 n.SetAttribute ("cref", cref);
3175                                                 n.InnerXml = value;
3176                                                 e.AppendChild (n);
3177                                         }
3178                                         break;
3179                                 }
3180                                 default: {
3181                                         string name = ecmadocs.Name;
3182                                         string xpath = ecmadocs.Name;
3183                                         StringList attributes = new StringList (ecmadocs.AttributeCount);
3184                                         if (ecmadocs.MoveToFirstAttribute ()) {
3185                                                 do {
3186                                                         attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3187                                                 } while (ecmadocs.MoveToNextAttribute ());
3188                                                 ecmadocs.MoveToContent ();
3189                                         }
3190                                         if (attributes.Count > 0) {
3191                                                 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3192                                         }
3193                                         XmlNode doc = e.SelectSingleNode (xpath);
3194                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3195                                         if (doc != null) {
3196                                                 doc.InnerXml = value;
3197                                         }
3198                                         else {
3199                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3200                                                 n.InnerXml = value;
3201                                                 foreach (string a in attributes) {
3202                                                         int eq = a.IndexOf ('=');
3203                                                         n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3204                                                 }
3205                                                 e.AppendChild (n);
3206                                         }
3207                                         break;
3208                                 }
3209                         }
3210                 }
3211         }
3212 }
3213
3214 class DocumentationMember {
3215         public StringToStringMap MemberSignatures = new StringToStringMap ();
3216         public string ReturnType;
3217         public StringList Parameters;
3218         public string MemberName;
3219         public string MemberType;
3220
3221         public DocumentationMember (XmlReader reader)
3222         {
3223                 MemberName = reader.GetAttribute ("MemberName");
3224                 int depth = reader.Depth;
3225                 bool go = true;
3226                 StringList p = new StringList ();
3227                 do {
3228                         if (reader.NodeType != XmlNodeType.Element)
3229                                 continue;
3230                         switch (reader.Name) {
3231                                 case "MemberSignature":
3232                                         MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3233                                         break;
3234                                 case "MemberType":
3235                                         MemberType = reader.ReadElementString ();
3236                                         break;
3237                                 case "ReturnType":
3238                                         if (reader.Depth == depth + 2)
3239                                                 ReturnType = reader.ReadElementString ();
3240                                         break;
3241                                 case "Parameter":
3242                                         if (reader.Depth == depth + 2)
3243                                                 p.Add (reader.GetAttribute ("Type"));
3244                                         break;
3245                                 case "Docs":
3246                                         if (reader.Depth == depth + 1)
3247                                                 go = false;
3248                                         break;
3249                         }
3250                 } while (go && reader.Read () && reader.Depth >= depth);
3251                 if (p.Count > 0) {
3252                         Parameters = p;
3253                 }
3254         }
3255
3256         public DocumentationMember (XmlNode node)
3257         {
3258                 MemberName = node.Attributes ["MemberName"].Value;
3259                 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3260                         XmlAttribute l = n.Attributes ["Language"];
3261                         XmlAttribute v = n.Attributes ["Value"];
3262                         if (l != null && v != null)
3263                                 MemberSignatures [l.Value] = v.Value;
3264                 }
3265                 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3266                 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3267                 if (rt != null)
3268                         ReturnType = rt.InnerText;
3269                 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3270                 if (p.Count > 0) {
3271                         Parameters = new StringList (p.Count);
3272                         for (int i = 0; i < p.Count; ++i)
3273                                 Parameters.Add (p [i].Attributes ["Type"].Value);
3274                 }
3275         }
3276 }
3277
3278 public class DynamicParserContext {
3279         public ReadOnlyCollection<bool> TransformFlags;
3280         public int TransformIndex;
3281
3282         public DynamicParserContext (ICustomAttributeProvider provider)
3283         {
3284                 CustomAttribute da;
3285                 if (provider.HasCustomAttributes &&
3286                                 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3287                                         .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3288                         CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3289                                 ? new CustomAttributeArgument [0]
3290                                 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3291
3292                         TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3293                 }
3294         }
3295 }
3296
3297 public enum MemberFormatterState {
3298         None,
3299         WithinGenericTypeParameters,
3300 }
3301
3302 public abstract class MemberFormatter {
3303
3304         public virtual string Language {
3305                 get {return "";}
3306         }
3307
3308         public string GetName (MemberReference member)
3309         {
3310                 return GetName (member, null);
3311         }
3312
3313         public virtual string GetName (MemberReference member, DynamicParserContext context)
3314         {
3315                 TypeReference type = member as TypeReference;
3316                 if (type != null)
3317                         return GetTypeName (type, context);
3318                 MethodReference method  = member as MethodReference;
3319                 if (method != null && method.Name == ".ctor") // method.IsConstructor
3320                         return GetConstructorName (method);
3321                 if (method != null)
3322                         return GetMethodName (method);
3323                 PropertyReference prop = member as PropertyReference;
3324                 if (prop != null)
3325                         return GetPropertyName (prop);
3326                 FieldReference field = member as FieldReference;
3327                 if (field != null)
3328                         return GetFieldName (field);
3329                 EventReference e = member as EventReference;
3330                 if (e != null)
3331                         return GetEventName (e);
3332                 throw new NotSupportedException ("Can't handle: " +
3333                                         (member == null ? "null" : member.GetType().ToString()));
3334         }
3335
3336         protected virtual string GetTypeName (TypeReference type)
3337         {
3338                 return GetTypeName (type, null);
3339         }
3340
3341         protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3342         {
3343                 if (type == null)
3344                         throw new ArgumentNullException ("type");
3345                 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3346         }
3347
3348         protected virtual char[] ArrayDelimeters {
3349                 get {return new char[]{'[', ']'};}
3350         }
3351
3352         protected virtual MemberFormatterState MemberFormatterState { get; set; }
3353
3354         protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3355         {
3356                 if (type is ArrayType) {
3357                         TypeSpecification spec = type as TypeSpecification;
3358                         _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3359                         return AppendArrayModifiers (buf, (ArrayType) type);
3360                 }
3361                 if (type is ByReferenceType) {
3362                         return AppendRefTypeName (buf, type, context);
3363                 }
3364                 if (type is PointerType) {
3365                         return AppendPointerTypeName (buf, type, context);
3366                 }
3367                 AppendNamespace (buf, type);
3368                 if (type is GenericParameter) {
3369                         return AppendTypeName (buf, type, context);
3370                 }
3371                 GenericInstanceType genInst = type as GenericInstanceType;
3372                 if (type.GenericParameters.Count == 0 &&
3373                                 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3374                         return AppendFullTypeName (buf, type, context);
3375                 }
3376                 return AppendGenericType (buf, type, context);
3377         }
3378
3379         protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3380         {
3381                 string ns = DocUtils.GetNamespace (type);
3382                 if (ns != null && ns.Length > 0)
3383                         buf.Append (ns).Append ('.');
3384                 return buf;
3385         }
3386
3387         protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3388         {
3389                 if (type.DeclaringType != null)
3390                         AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3391                 return AppendTypeName (buf, type, context);
3392         }
3393
3394         protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3395         {
3396                 if (context != null)
3397                         context.TransformIndex++;
3398                 return AppendTypeName (buf, type.Name);
3399         }
3400
3401         protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3402         {
3403                 int n = typename.IndexOf ("`");
3404                 if (n >= 0)
3405                         return buf.Append (typename.Substring (0, n));
3406                 return buf.Append (typename);
3407         }
3408
3409         protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3410         {
3411                 buf.Append (ArrayDelimeters [0]);
3412                 int rank = array.Rank;
3413                 if (rank > 1)
3414                         buf.Append (new string (',', rank-1));
3415                 return buf.Append (ArrayDelimeters [1]);
3416         }
3417
3418         protected virtual string RefTypeModifier {
3419                 get {return "@";}
3420         }
3421
3422         protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3423         {
3424                 TypeSpecification spec = type as TypeSpecification;
3425                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3426                                 .Append (RefTypeModifier);
3427         }
3428
3429         protected virtual string PointerModifier {
3430                 get {return "*";}
3431         }
3432
3433         protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3434         {
3435                 TypeSpecification spec = type as TypeSpecification;
3436                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3437                                 .Append (PointerModifier);
3438         }
3439
3440         protected virtual char[] GenericTypeContainer {
3441                 get {return new char[]{'<', '>'};}
3442         }
3443
3444         protected virtual char NestedTypeSeparator {
3445                 get {return '.';}
3446         }
3447
3448         protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3449         {
3450                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3451                                 type is GenericInstanceType ? type.GetElementType () : type);
3452                 List<TypeReference> genArgs = GetGenericArguments (type);
3453                 int argIdx = 0;
3454                 int prev = 0;
3455                 bool insertNested = false;
3456                 foreach (var decl in decls) {
3457                         TypeReference declDef = decl.Resolve () ?? decl;
3458                         if (insertNested) {
3459                                 buf.Append (NestedTypeSeparator);
3460                         }
3461                         insertNested = true;
3462                         AppendTypeName (buf, declDef, context);
3463                         int ac = DocUtils.GetGenericArgumentCount (declDef);
3464                         int c = ac - prev;
3465                         prev = ac;
3466                         if (c > 0) {
3467                                 buf.Append (GenericTypeContainer [0]);
3468                                 var origState = MemberFormatterState;
3469                                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3470                                 _AppendTypeName (buf, genArgs [argIdx++], context);
3471                                 for (int i = 1; i < c; ++i) {
3472                                         _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3473                                 }
3474                                 MemberFormatterState = origState;
3475                                 buf.Append (GenericTypeContainer [1]);
3476                         }
3477                 }
3478                 return buf;
3479         }
3480
3481         protected List<TypeReference> GetGenericArguments (TypeReference type)
3482         {
3483                 var args = new List<TypeReference> ();
3484                 GenericInstanceType inst = type as GenericInstanceType;
3485                 if (inst != null)
3486                         args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3487                 else
3488                         args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3489                 return args;
3490         }
3491
3492         protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3493         {
3494                 return buf;
3495         }
3496
3497         protected virtual string GetConstructorName (MethodReference constructor)
3498         {
3499                 return constructor.Name;
3500         }
3501
3502         protected virtual string GetMethodName (MethodReference method)
3503         {
3504                 return method.Name;
3505         }
3506
3507         protected virtual string GetPropertyName (PropertyReference property)
3508         {
3509                 return property.Name;
3510         }
3511
3512         protected virtual string GetFieldName (FieldReference field)
3513         {
3514                 return field.Name;
3515         }
3516
3517         protected virtual string GetEventName (EventReference e)
3518         {
3519                 return e.Name;
3520         }
3521
3522         public virtual string GetDeclaration (MemberReference member)
3523         {
3524                 if (member == null)
3525                         throw new ArgumentNullException ("member");
3526                 TypeDefinition type = member as TypeDefinition;
3527                 if (type != null)
3528                         return GetTypeDeclaration (type);
3529                 MethodDefinition method = member as MethodDefinition;
3530                 if (method != null && method.IsConstructor)
3531                         return GetConstructorDeclaration (method);
3532                 if (method != null)
3533                         return GetMethodDeclaration (method);
3534                 PropertyDefinition prop = member as PropertyDefinition;
3535                 if (prop != null)
3536                         return GetPropertyDeclaration (prop);
3537                 FieldDefinition field = member as FieldDefinition;
3538                 if (field != null)
3539                         return GetFieldDeclaration (field);
3540                 EventDefinition e = member as EventDefinition;
3541                 if (e != null)
3542                         return GetEventDeclaration (e);
3543                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3544         }
3545
3546         protected virtual string GetTypeDeclaration (TypeDefinition type)
3547         {
3548                 if (type == null)
3549                         throw new ArgumentNullException ("type");
3550                 StringBuilder buf = new StringBuilder (type.Name.Length);
3551                 _AppendTypeName (buf, type, null);
3552                 AppendGenericTypeConstraints (buf, type);
3553                 return buf.ToString ();
3554         }
3555
3556         protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3557         {
3558                 return GetConstructorName (constructor);
3559         }
3560
3561         protected virtual string GetMethodDeclaration (MethodDefinition method)
3562         {
3563                 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3564                                         ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3565                         return null;
3566
3567                 // Special signature for destructors.
3568                 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3569                         return GetFinalizerName (method);
3570
3571                 StringBuilder buf = new StringBuilder ();
3572
3573                 AppendVisibility (buf, method);
3574                 if (buf.Length == 0 && 
3575                                 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3576                         return null;
3577
3578                 AppendModifiers (buf, method);
3579
3580                 if (buf.Length != 0)
3581                         buf.Append (" ");
3582                 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3583
3584                 AppendMethodName (buf, method);
3585                 AppendGenericMethod (buf, method).Append (" ");
3586                 AppendParameters (buf, method, method.Parameters);
3587                 AppendGenericMethodConstraints (buf, method);
3588                 return buf.ToString ();
3589         }
3590
3591         protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3592         {
3593                 return buf.Append (method.Name);
3594         }
3595
3596         protected virtual string GetFinalizerName (MethodDefinition method)
3597         {
3598                 return "Finalize";
3599         }
3600
3601         protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3602         {
3603                 return buf;
3604         }
3605
3606         protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3607         {
3608                 return buf;
3609         }
3610
3611         protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3612         {
3613                 return buf;
3614         }
3615
3616         protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3617         {
3618                 return buf;
3619         }
3620
3621         protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3622         {
3623                 return buf;
3624         }
3625
3626         protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3627         {
3628                 return GetPropertyName (property);
3629         }
3630
3631         protected virtual string GetFieldDeclaration (FieldDefinition field)
3632         {
3633                 return GetFieldName (field);
3634         }
3635
3636         protected virtual string GetEventDeclaration (EventDefinition e)
3637         {
3638                 return GetEventName (e);
3639         }
3640 }
3641
3642 class ILFullMemberFormatter : MemberFormatter {
3643
3644         public override string Language {
3645                 get {return "ILAsm";}
3646         }
3647
3648         protected override char NestedTypeSeparator {
3649                 get {
3650                         return '/';
3651                 }
3652         }
3653
3654         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3655         {
3656                 if (GetBuiltinType (type.FullName) != null)
3657                         return buf;
3658                 string ns = DocUtils.GetNamespace (type);
3659                 if (ns != null && ns.Length > 0) {
3660                         if (type.IsValueType)
3661                                 buf.Append ("valuetype ");
3662                         else
3663                                 buf.Append ("class ");
3664                         buf.Append (ns).Append ('.');
3665                 }
3666                 return buf;
3667         }
3668
3669         private static string GetBuiltinType (string t)
3670         {
3671                 switch (t) {
3672                 case "System.Byte":    return "unsigned int8";
3673                 case "System.SByte":   return "int8";
3674                 case "System.Int16":   return "int16";
3675                 case "System.Int32":   return "int32";
3676                 case "System.Int64":   return "int64";
3677                 case "System.IntPtr":  return "native int";
3678
3679                 case "System.UInt16":  return "unsigned int16";
3680                 case "System.UInt32":  return "unsigned int32";
3681                 case "System.UInt64":  return "unsigned int64";
3682                 case "System.UIntPtr": return "native unsigned int";
3683
3684                 case "System.Single":  return "float32";
3685                 case "System.Double":  return "float64";
3686                 case "System.Boolean": return "bool";
3687                 case "System.Char":    return "char";
3688                 case "System.Void":    return "void";
3689                 case "System.String":  return "string";
3690                 case "System.Object":  return "object";
3691                 }
3692                 return null;
3693         }
3694
3695         protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3696         {
3697                 return buf.Append (typename);
3698         }
3699
3700         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3701         {
3702                 if (type is GenericParameter) {
3703                         AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3704                         return buf;
3705                 }
3706
3707                 string s = GetBuiltinType (type.FullName);
3708                 if (s != null) {
3709                         return buf.Append (s);
3710                 }
3711                 return base.AppendTypeName (buf, type, context);
3712         }
3713
3714         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3715         {
3716                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3717                         return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3718                 }
3719                 GenericParameterAttributes attrs = type.Attributes;
3720                 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3721                         buf.Append ("class ");
3722                 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3723                         buf.Append ("struct ");
3724                 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3725                         buf.Append (".ctor ");
3726                 IList<TypeReference> constraints = type.Constraints;
3727                 MemberFormatterState = 0;
3728                 if (constraints.Count > 0) {
3729                         var full = new ILFullMemberFormatter ();
3730                         buf.Append ("(").Append (full.GetName (constraints [0]));
3731                         for (int i = 1; i < constraints.Count; ++i) {
3732                                 buf.Append (", ").Append (full.GetName (constraints [i]));
3733                         }
3734                         buf.Append (") ");
3735                 }
3736                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3737
3738                 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3739                         buf.Append ("+ ");
3740                 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3741                         buf.Append ("- ");
3742                 return buf;
3743         }
3744
3745         protected override string GetTypeDeclaration (TypeDefinition type)
3746         {
3747                 string visibility = GetTypeVisibility (type.Attributes);
3748                 if (visibility == null)
3749                         return null;
3750
3751                 StringBuilder buf = new StringBuilder ();
3752
3753                 buf.Append (".class ");
3754                 if (type.IsNested)
3755                         buf.Append ("nested ");
3756                 buf.Append (visibility).Append (" ");
3757                 if (type.IsInterface)
3758                         buf.Append ("interface ");
3759                 if (type.IsSequentialLayout)
3760                         buf.Append ("sequential ");
3761                 if (type.IsAutoLayout)
3762                         buf.Append ("auto ");
3763                 if (type.IsAnsiClass)
3764                         buf.Append ("ansi ");
3765                 if (type.IsAbstract)
3766                         buf.Append ("abstract ");
3767                 if (type.IsSerializable)
3768                         buf.Append ("serializable ");
3769                 if (type.IsSealed)
3770                         buf.Append ("sealed ");
3771                 if (type.IsBeforeFieldInit)
3772                         buf.Append ("beforefieldinit ");
3773                 var state = MemberFormatterState;
3774                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3775                 buf.Append (GetName (type));
3776                 MemberFormatterState = state;
3777                 var full = new ILFullMemberFormatter ();
3778                 if (type.BaseType != null) {
3779                         buf.Append (" extends ");
3780                         if (type.BaseType.FullName == "System.Object")
3781                                 buf.Append ("System.Object");
3782                         else
3783                                 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3784                 }
3785                 bool first = true;
3786                 foreach (var name in type.Interfaces
3787                                 .Select (i => full.GetName (i))
3788                                 .OrderBy (n => n)) {
3789                         if (first) {
3790                                 buf.Append (" implements ");
3791                                 first = false;
3792                         }
3793                         else {
3794                                 buf.Append (", ");
3795                         }
3796                         buf.Append (name);
3797                 }
3798
3799                 return buf.ToString ();
3800         }
3801
3802         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3803         {
3804                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3805                                 type is GenericInstanceType ? type.GetElementType () : type);
3806                 bool first = true;
3807                 foreach (var decl in decls) {
3808                         TypeReference declDef = decl.Resolve () ?? decl;
3809                         if (!first) {
3810                                 buf.Append (NestedTypeSeparator);
3811                         }
3812                         first = false;
3813                         AppendTypeName (buf, declDef, context);
3814                 }
3815                 buf.Append ('<');
3816                 first = true;
3817                 foreach (TypeReference arg in GetGenericArguments (type)) {
3818                         if (!first)
3819                                 buf.Append (", ");
3820                         first = false;
3821                         _AppendTypeName (buf, arg, context);
3822                 }
3823                 buf.Append ('>');
3824                 return buf;
3825         }
3826
3827         static string GetTypeVisibility (TypeAttributes ta)
3828         {
3829                 switch (ta & TypeAttributes.VisibilityMask) {
3830                 case TypeAttributes.Public:
3831                 case TypeAttributes.NestedPublic:
3832                         return "public";
3833
3834                 case TypeAttributes.NestedFamily:
3835                 case TypeAttributes.NestedFamORAssem:
3836                         return "protected";
3837
3838                 default:
3839                         return null;
3840                 }
3841         }
3842
3843         protected override string GetConstructorDeclaration (MethodDefinition constructor)
3844         {
3845                 return GetMethodDeclaration (constructor);
3846         }
3847
3848         protected override string GetMethodDeclaration (MethodDefinition method)
3849         {
3850                 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3851                         return null;
3852
3853                 var buf = new StringBuilder ();
3854                 buf.Append (".method ");
3855                 AppendVisibility (buf, method);
3856                 if (method.IsStatic)
3857                         buf.Append ("static ");
3858                 if (method.IsHideBySig)
3859                         buf.Append ("hidebysig ");
3860                 if (method.IsPInvokeImpl) {
3861                         var info = method.PInvokeInfo;
3862                         buf.Append ("pinvokeimpl (\"")
3863                                 .Append (info.Module.Name)
3864                                 .Append ("\" as \"")
3865                                 .Append (info.EntryPoint)
3866                                 .Append ("\"");
3867                         if (info.IsCharSetAuto)
3868                                 buf.Append (" auto");
3869                         if (info.IsCharSetUnicode)
3870                                 buf.Append (" unicode");
3871                         if (info.IsCharSetAnsi)
3872                                 buf.Append (" ansi");
3873                         if (info.IsCallConvCdecl)
3874                                 buf.Append (" cdecl");
3875                         if (info.IsCallConvStdCall)
3876                                 buf.Append (" stdcall");
3877                         if (info.IsCallConvWinapi)
3878                                 buf.Append (" winapi");
3879                         if (info.IsCallConvThiscall)
3880                                 buf.Append (" thiscall");
3881                         if (info.SupportsLastError)
3882                                 buf.Append (" lasterr");
3883                         buf.Append (")");
3884                 }
3885                 if (method.IsSpecialName)
3886                         buf.Append ("specialname ");
3887                 if (method.IsRuntimeSpecialName)
3888                         buf.Append ("rtspecialname ");
3889                 if (method.IsNewSlot)
3890                         buf.Append ("newslot ");
3891                 if (method.IsVirtual)
3892                         buf.Append ("virtual ");
3893                 if (!method.IsStatic)
3894                         buf.Append ("instance ");
3895                 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3896                 buf.Append (' ')
3897                         .Append (method.Name);
3898                 if (method.IsGenericMethod ()) {
3899                         var state = MemberFormatterState;
3900                         MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3901                         IList<GenericParameter> args = method.GenericParameters;
3902                         if (args.Count > 0) {
3903                                 buf.Append ("<");
3904                                 _AppendTypeName (buf, args [0], null);
3905                                 for (int i = 1; i < args.Count; ++i)
3906                                         _AppendTypeName (buf.Append (", "), args [i], null);
3907                                 buf.Append (">");
3908                         }
3909                         MemberFormatterState = state;
3910                 }
3911
3912                 buf.Append ('(');
3913                 bool first = true;
3914                 for (int i = 0; i < method.Parameters.Count; ++i) {
3915                         if (!first)
3916                                 buf.Append (", ");
3917                         first = false;
3918                         _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3919                         buf.Append (' ');
3920                         buf.Append (method.Parameters [i].Name);
3921                 }
3922                 buf.Append (')');
3923                 if (method.IsIL)
3924                         buf.Append (" cil");
3925                 if (method.IsRuntime)
3926                         buf.Append (" runtime");
3927                 if (method.IsManaged)
3928                         buf.Append (" managed");
3929
3930                 return buf.ToString ();
3931         }
3932
3933         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3934         {
3935                 if (DocUtils.IsExplicitlyImplemented (method)) {
3936                         TypeReference iface;
3937                         MethodReference ifaceMethod;
3938                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3939                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3940                                 .Append ('.')
3941                                 .Append (ifaceMethod.Name);
3942                 }
3943                 return base.AppendMethodName (buf, method);
3944         }
3945
3946         protected override string RefTypeModifier {
3947                 get {return "";}
3948         }
3949
3950         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3951         {
3952                 if (method.IsPublic)
3953                         return buf.Append ("public ");
3954                 if (method.IsFamilyAndAssembly)
3955                         return buf.Append ("familyandassembly");
3956                 if (method.IsFamilyOrAssembly)
3957                         return buf.Append ("familyorassembly");
3958                 if (method.IsFamily)
3959                         return buf.Append ("family");
3960                 return buf;
3961         }
3962
3963         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3964         {
3965                 string modifiers = String.Empty;
3966                 if (method.IsStatic) modifiers += " static";
3967                 if (method.IsVirtual && !method.IsAbstract) {
3968                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3969                         else modifiers += " override";
3970                 }
3971                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3972                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3973                 if (method.IsFinal) modifiers += " sealed";
3974                 if (modifiers == " virtual sealed") modifiers = "";
3975
3976                 return buf.Append (modifiers);
3977         }
3978
3979         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3980         {
3981                 if (method.IsGenericMethod ()) {
3982                         IList<GenericParameter> args = method.GenericParameters;
3983                         if (args.Count > 0) {
3984                                 buf.Append ("<");
3985                                 buf.Append (args [0].Name);
3986                                 for (int i = 1; i < args.Count; ++i)
3987                                         buf.Append (",").Append (args [i].Name);
3988                                 buf.Append (">");
3989                         }
3990                 }
3991                 return buf;
3992         }
3993
3994         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3995         {
3996                 return AppendParameters (buf, method, parameters, '(', ')');
3997         }
3998
3999         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4000         {
4001                 buf.Append (begin);
4002
4003                 if (parameters.Count > 0) {
4004                         if (DocUtils.IsExtensionMethod (method))
4005                                 buf.Append ("this ");
4006                         AppendParameter (buf, parameters [0]);
4007                         for (int i = 1; i < parameters.Count; ++i) {
4008                                 buf.Append (", ");
4009                                 AppendParameter (buf, parameters [i]);
4010                         }
4011                 }
4012
4013                 return buf.Append (end);
4014         }
4015
4016         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4017         {
4018                 if (parameter.ParameterType is ByReferenceType) {
4019                         if (parameter.IsOut)
4020                                 buf.Append ("out ");
4021                         else
4022                                 buf.Append ("ref ");
4023                 }
4024                 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4025                 return buf.Append (parameter.Name);
4026         }
4027
4028         protected override string GetPropertyDeclaration (PropertyDefinition property)
4029         {
4030                 MethodDefinition gm = null, sm = null;
4031
4032                 string get_visible = null;
4033                 if ((gm = property.GetMethod) != null &&
4034                                 (DocUtils.IsExplicitlyImplemented (gm) ||
4035                                  (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4036                         get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4037                 string set_visible = null;
4038                 if ((sm = property.SetMethod) != null &&
4039                                 (DocUtils.IsExplicitlyImplemented (sm) ||
4040                                  (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4041                         set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4042
4043                 if ((set_visible == null) && (get_visible == null))
4044                         return null;
4045
4046                 StringBuilder buf = new StringBuilder ()
4047                         .Append (".property ");
4048                 if (!(gm ?? sm).IsStatic)
4049                         buf.Append ("instance ");
4050                 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4051                 buf.Append (' ').Append (property.Name);
4052                 if (!property.HasParameters || property.Parameters.Count == 0)
4053                         return buf.ToString ();
4054
4055                 buf.Append ('(');
4056                 bool first = true;
4057                 foreach (ParameterDefinition p in property.Parameters) {
4058                         if (!first)
4059                                 buf.Append (", ");
4060                         first = false;
4061                         _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4062                 }
4063                 buf.Append (')');
4064
4065                 return buf.ToString ();
4066         }
4067
4068         protected override string GetFieldDeclaration (FieldDefinition field)
4069         {
4070                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4071                 if (declType.IsEnum && field.Name == "value__")
4072                         return null; // This member of enums aren't documented.
4073
4074                 StringBuilder buf = new StringBuilder ();
4075                 AppendFieldVisibility (buf, field);
4076                 if (buf.Length == 0)
4077                         return null;
4078
4079                 buf.Insert (0, ".field ");
4080
4081                 if (field.IsStatic)
4082                         buf.Append ("static ");
4083                 if (field.IsInitOnly)
4084                         buf.Append ("initonly ");
4085                 if (field.IsLiteral)
4086                         buf.Append ("literal ");
4087                 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4088                 buf.Append (' ').Append (field.Name);
4089                 AppendFieldValue (buf, field);
4090
4091                 return buf.ToString ();
4092         }
4093
4094         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4095         {
4096                 if (field.IsPublic)
4097                         return buf.Append ("public ");
4098                 if (field.IsFamilyAndAssembly)
4099                         return buf.Append ("familyandassembly ");
4100                 if (field.IsFamilyOrAssembly)
4101                         return buf.Append ("familyorassembly ");
4102                 if (field.IsFamily)
4103                         return buf.Append ("family ");
4104                 return buf;
4105         }
4106
4107         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4108         {
4109                 // enums have a value__ field, which we ignore
4110                 if (field.DeclaringType.IsGenericType ())
4111                         return buf;
4112                 if (field.HasConstant && field.IsLiteral) {
4113                         object val = null;
4114                         try {
4115                                 val   = field.Constant;
4116                         } catch {
4117                                 return buf;
4118                         }
4119                         if (val == null)
4120                                 buf.Append (" = ").Append ("null");
4121                         else if (val is Enum)
4122                                 buf.Append (" = ")
4123                                         .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4124                                         .Append ('(')
4125                                         .Append (val.ToString ())
4126                                         .Append (')');
4127                         else if (val is IFormattable) {
4128                                 string value = ((IFormattable)val).ToString();
4129                                 buf.Append (" = ");
4130                                 if (val is string)
4131                                         buf.Append ("\"" + value + "\"");
4132                                 else
4133                                         buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4134                                                 .Append ('(')
4135                                                 .Append (value)
4136                                                 .Append (')');
4137                         }
4138                 }
4139                 return buf;
4140         }
4141
4142         protected override string GetEventDeclaration (EventDefinition e)
4143         {
4144                 StringBuilder buf = new StringBuilder ();
4145                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4146                         return null;
4147                 }
4148
4149                 buf.Length = 0;
4150                 buf.Append (".event ")
4151                         .Append (GetName (e.EventType))
4152                         .Append (' ')
4153                         .Append (e.Name);
4154
4155                 return buf.ToString ();
4156         }
4157 }
4158
4159 class ILMemberFormatter : ILFullMemberFormatter {
4160         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4161         {
4162                 return buf;
4163         }
4164 }
4165
4166 class CSharpFullMemberFormatter : MemberFormatter {
4167
4168         public override string Language {
4169                 get {return "C#";}
4170         }
4171
4172         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4173         {
4174                 string ns = DocUtils.GetNamespace (type);
4175                 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4176                         buf.Append (ns).Append ('.');
4177                 return buf;
4178         }
4179
4180         private string GetCSharpType (string t)
4181         {
4182                 switch (t) {
4183                 case "System.Byte":    return "byte";
4184                 case "System.SByte":   return "sbyte";
4185                 case "System.Int16":   return "short";
4186                 case "System.Int32":   return "int";
4187                 case "System.Int64":   return "long";
4188
4189                 case "System.UInt16":  return "ushort";
4190                 case "System.UInt32":  return "uint";
4191                 case "System.UInt64":  return "ulong";
4192
4193                 case "System.Single":  return "float";
4194                 case "System.Double":  return "double";
4195                 case "System.Decimal": return "decimal";
4196                 case "System.Boolean": return "bool";
4197                 case "System.Char":    return "char";
4198                 case "System.Void":    return "void";
4199                 case "System.String":  return "string";
4200                 case "System.Object":  return "object";
4201                 }
4202                 return null;
4203         }
4204
4205         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4206         {
4207                 if (context != null && context.TransformFlags != null &&
4208                                 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4209                         context.TransformIndex++;
4210                         return buf.Append ("dynamic");
4211                 }
4212
4213                 if (type is GenericParameter)
4214                         return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4215                 string t = type.FullName;
4216                 if (!t.StartsWith ("System.")) {
4217                         return base.AppendTypeName (buf, type, context);
4218                 }
4219
4220                 string s = GetCSharpType (t);
4221                 if (s != null) {
4222                         if (context != null)
4223                                 context.TransformIndex++;
4224                         return buf.Append (s);
4225                 }
4226                 
4227                 return base.AppendTypeName (buf, type, context);
4228         }
4229
4230         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4231         {
4232                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4233                         return buf;
4234                 GenericParameterAttributes attrs = type.Attributes;
4235                 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4236                 bool isin  = (attrs & GenericParameterAttributes.Contravariant) != 0;
4237                 if (isin)
4238                         buf.Append ("in ");
4239                 else if (isout)
4240                         buf.Append ("out ");
4241                 return buf;
4242         }
4243
4244         protected override string GetTypeDeclaration (TypeDefinition type)
4245         {
4246                 string visibility = GetTypeVisibility (type.Attributes);
4247                 if (visibility == null)
4248                         return null;
4249
4250                 StringBuilder buf = new StringBuilder ();
4251                 
4252                 buf.Append (visibility);
4253                 buf.Append (" ");
4254
4255                 MemberFormatter full = new CSharpFullMemberFormatter ();
4256
4257                 if (DocUtils.IsDelegate (type)) {
4258                         buf.Append("delegate ");
4259                         MethodDefinition invoke = type.GetMethod ("Invoke");
4260                         buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4261                         buf.Append (GetName (type));
4262                         AppendParameters (buf, invoke, invoke.Parameters);
4263                         AppendGenericTypeConstraints (buf, type);
4264                         buf.Append (";");
4265
4266                         return buf.ToString();
4267                 }
4268                 
4269                 if (type.IsAbstract && !type.IsInterface)
4270                         buf.Append("abstract ");
4271                 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4272                         buf.Append("sealed ");
4273                 buf.Replace ("abstract sealed", "static");
4274
4275                 buf.Append (GetTypeKind (type));
4276                 buf.Append (" ");
4277                 buf.Append (GetCSharpType (type.FullName) == null 
4278                                 ? GetName (type) 
4279                                 : type.Name);
4280
4281                 if (!type.IsEnum) {
4282                         TypeReference basetype = type.BaseType;
4283                         if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType)       // FIXME
4284                                 basetype = null;
4285
4286                         List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4287                                         .Select (iface => full.GetName (iface))
4288                                         .OrderBy (s => s)
4289                                         .ToList ();
4290
4291                         if (basetype != null || interface_names.Count > 0)
4292                                 buf.Append (" : ");
4293                         
4294                         if (basetype != null) {
4295                                 buf.Append (full.GetName (basetype));
4296                                 if (interface_names.Count > 0)
4297                                         buf.Append (", ");
4298                         }
4299                         
4300                         for (int i = 0; i < interface_names.Count; i++){
4301                                 if (i != 0)
4302                                         buf.Append (", ");
4303                                 buf.Append (interface_names [i]);
4304                         }
4305                         AppendGenericTypeConstraints (buf, type);
4306                 }
4307
4308                 return buf.ToString ();
4309         }
4310
4311         static string GetTypeKind (TypeDefinition t)
4312         {
4313                 if (t.IsEnum)
4314                         return "enum";
4315                 if (t.IsValueType)
4316                         return "struct";
4317                 if (t.IsClass || t.FullName == "System.Enum")
4318                         return "class";
4319                 if (t.IsInterface)
4320                         return "interface";
4321                 throw new ArgumentException(t.FullName);
4322         }
4323
4324         static string GetTypeVisibility (TypeAttributes ta)
4325         {
4326                 switch (ta & TypeAttributes.VisibilityMask) {
4327                 case TypeAttributes.Public:
4328                 case TypeAttributes.NestedPublic:
4329                         return "public";
4330
4331                 case TypeAttributes.NestedFamily:
4332                 case TypeAttributes.NestedFamORAssem:
4333                         return "protected";
4334
4335                 default:
4336                         return null;
4337                 }
4338         }
4339
4340         protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4341         {
4342                 if (type.GenericParameters.Count == 0)
4343                         return buf;
4344                 return AppendConstraints (buf, type.GenericParameters);
4345         }
4346
4347         private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4348         {
4349                 foreach (GenericParameter genArg in genArgs) {
4350                         GenericParameterAttributes attrs = genArg.Attributes;
4351                         IList<TypeReference> constraints = genArg.Constraints;
4352                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4353                                 continue;
4354
4355                         bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4356                         bool isvt  = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4357                         bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4358                         bool comma = false;
4359
4360                         if (!isref && !isvt && !isnew && constraints.Count == 0)
4361                                 continue;
4362                         buf.Append (" where ").Append (genArg.Name).Append (" : ");
4363                         if (isref) {
4364                                 buf.Append ("class");
4365                                 comma = true;
4366                         }
4367                         else if (isvt) {
4368                                 buf.Append ("struct");
4369                                 comma = true;
4370                         }
4371                         if (constraints.Count > 0 && !isvt) {
4372                                 if (comma)
4373                                         buf.Append (", ");
4374                                 buf.Append (GetTypeName (constraints [0]));
4375                                 for (int i = 1; i < constraints.Count; ++i)
4376                                         buf.Append (", ").Append (GetTypeName (constraints [i]));
4377                         }
4378                         if (isnew && !isvt) {
4379                                 if (comma)
4380                                         buf.Append (", ");
4381                                 buf.Append ("new()");
4382                         }
4383                 }
4384                 return buf;
4385         }
4386
4387         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4388         {
4389                 StringBuilder buf = new StringBuilder ();
4390                 AppendVisibility (buf, constructor);
4391                 if (buf.Length == 0)
4392                         return null;
4393
4394                 buf.Append (' ');
4395                 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4396                 AppendParameters (buf, constructor, constructor.Parameters);
4397                 buf.Append (';');
4398
4399                 return buf.ToString ();
4400         }
4401         
4402         protected override string GetMethodDeclaration (MethodDefinition method)
4403         {
4404                 string decl = base.GetMethodDeclaration (method);
4405                 if (decl != null)
4406                         return decl + ";";
4407                 return null;
4408         }
4409
4410         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4411         {
4412                 if (DocUtils.IsExplicitlyImplemented (method)) {
4413                         TypeReference iface;
4414                         MethodReference ifaceMethod;
4415                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4416                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4417                                 .Append ('.')
4418                                 .Append (ifaceMethod.Name);
4419                 }
4420                 return base.AppendMethodName (buf, method);
4421         }
4422
4423         protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4424         {
4425                 if (method.GenericParameters.Count == 0)
4426                         return buf;
4427                 return AppendConstraints (buf, method.GenericParameters);
4428         }
4429
4430         protected override string RefTypeModifier {
4431                 get {return "";}
4432         }
4433
4434         protected override string GetFinalizerName (MethodDefinition method)
4435         {
4436                 return "~" + method.DeclaringType.Name + " ()"; 
4437         }
4438
4439         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4440         {
4441                 if (method == null)
4442                         return buf;
4443                 if (method.IsPublic)
4444                         return buf.Append ("public");
4445                 if (method.IsFamily || method.IsFamilyOrAssembly)
4446                         return buf.Append ("protected");
4447                 return buf;
4448         }
4449
4450         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4451         {
4452                 string modifiers = String.Empty;
4453                 if (method.IsStatic) modifiers += " static";
4454                 if (method.IsVirtual && !method.IsAbstract) {
4455                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4456                         else modifiers += " override";
4457                 }
4458                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4459                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4460                 if (method.IsFinal) modifiers += " sealed";
4461                 if (modifiers == " virtual sealed") modifiers = "";
4462
4463                 return buf.Append (modifiers);
4464         }
4465
4466         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4467         {
4468                 if (method.IsGenericMethod ()) {
4469                         IList<GenericParameter> args = method.GenericParameters;
4470                         if (args.Count > 0) {
4471                                 buf.Append ("<");
4472                                 buf.Append (args [0].Name);
4473                                 for (int i = 1; i < args.Count; ++i)
4474                                         buf.Append (",").Append (args [i].Name);
4475                                 buf.Append (">");
4476                         }
4477                 }
4478                 return buf;
4479         }
4480
4481         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4482         {
4483                 return AppendParameters (buf, method, parameters, '(', ')');
4484         }
4485
4486         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4487         {
4488                 buf.Append (begin);
4489
4490                 if (parameters.Count > 0) {
4491                         if (DocUtils.IsExtensionMethod (method))
4492                                 buf.Append ("this ");
4493                         AppendParameter (buf, parameters [0]);
4494                         for (int i = 1; i < parameters.Count; ++i) {
4495                                 buf.Append (", ");
4496                                 AppendParameter (buf, parameters [i]);
4497                         }
4498                 }
4499
4500                 return buf.Append (end);
4501         }
4502
4503         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4504         {
4505                 if (parameter.ParameterType is ByReferenceType) {
4506                         if (parameter.IsOut)
4507                                 buf.Append ("out ");
4508                         else
4509                                 buf.Append ("ref ");
4510                 }
4511                 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4512                 buf.Append (parameter.Name);
4513                 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4514                         buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4515                 }
4516                 return buf;
4517         }
4518
4519         protected override string GetPropertyDeclaration (PropertyDefinition property)
4520         {
4521                 MethodDefinition method;
4522
4523                 string get_visible = null;
4524                 if ((method = property.GetMethod) != null && 
4525                                 (DocUtils.IsExplicitlyImplemented (method) || 
4526                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4527                         get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4528                 string set_visible = null;
4529                 if ((method = property.SetMethod) != null &&
4530                                 (DocUtils.IsExplicitlyImplemented (method) || 
4531                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4532                         set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4533
4534                 if ((set_visible == null) && (get_visible == null))
4535                         return null;
4536
4537                 string visibility;
4538                 StringBuilder buf = new StringBuilder ();
4539                 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4540                         buf.Append (visibility = get_visible);
4541                 else if (set_visible != null && get_visible == null)
4542                         buf.Append (visibility = set_visible);
4543                 else
4544                         buf.Append (visibility = "public");
4545
4546                 // Pick an accessor to use for static/virtual/override/etc. checks.
4547                 method = property.SetMethod;
4548                 if (method == null)
4549                         method = property.GetMethod;
4550         
4551                 string modifiers = String.Empty;
4552                 if (method.IsStatic) modifiers += " static";
4553                 if (method.IsVirtual && !method.IsAbstract) {
4554                                 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4555                                         modifiers += " virtual";
4556                                 else
4557                                         modifiers += " override";
4558                 }
4559                 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4560                 if (method.IsAbstract && !declDef.IsInterface)
4561                         modifiers += " abstract";
4562                 if (method.IsFinal)
4563                         modifiers += " sealed";
4564                 if (modifiers == " virtual sealed")
4565                         modifiers = "";
4566                 buf.Append (modifiers).Append (' ');
4567
4568                 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4569
4570                 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4571                 string name = property.Name;
4572                 foreach (MemberReference mi in defs) {
4573                         if (mi == property) {
4574                                 name = "this";
4575                                 break;
4576                         }
4577                 }
4578                 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4579         
4580                 if (property.Parameters.Count != 0) {
4581                         AppendParameters (buf, method, property.Parameters, '[', ']');
4582                 }
4583
4584                 buf.Append (" {");
4585                 if (get_visible != null) {
4586                         if (get_visible != visibility)
4587                                 buf.Append (' ').Append (get_visible);
4588                         buf.Append (" get;");
4589                 }
4590                 if (set_visible != null) {
4591                         if (set_visible != visibility)
4592                                 buf.Append (' ').Append (set_visible);
4593                         buf.Append (" set;");
4594                 }
4595                 buf.Append (" }");
4596         
4597                 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4598         }
4599
4600         protected override string GetFieldDeclaration (FieldDefinition field)
4601         {
4602                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4603                 if (declType.IsEnum && field.Name == "value__")
4604                         return null; // This member of enums aren't documented.
4605
4606                 StringBuilder buf = new StringBuilder ();
4607                 AppendFieldVisibility (buf, field);
4608                 if (buf.Length == 0)
4609                         return null;
4610
4611                 if (declType.IsEnum)
4612                         return field.Name;
4613
4614                 if (field.IsStatic && !field.IsLiteral)
4615                         buf.Append (" static");
4616                 if (field.IsInitOnly)
4617                         buf.Append (" readonly");
4618                 if (field.IsLiteral)
4619                         buf.Append (" const");
4620
4621                 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4622                 buf.Append (field.Name);
4623                 AppendFieldValue (buf, field);
4624                 buf.Append (';');
4625
4626                 return buf.ToString ();
4627         }
4628
4629         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4630         {
4631                 if (field.IsPublic)
4632                         return buf.Append ("public");
4633                 if (field.IsFamily || field.IsFamilyOrAssembly)
4634                         return buf.Append ("protected");
4635                 return buf;
4636         }
4637
4638         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4639         {
4640                 // enums have a value__ field, which we ignore
4641                 if (((TypeDefinition ) field.DeclaringType).IsEnum || 
4642                                 field.DeclaringType.IsGenericType ())
4643                         return buf;
4644                 if (field.HasConstant && field.IsLiteral) {
4645                         object val = null;
4646                         try {
4647                                 val   = field.Constant;
4648                         } catch {
4649                                 return buf;
4650                         }
4651                         if (val == null)
4652                                 buf.Append (" = ").Append ("null");
4653                         else if (val is Enum)
4654                                 buf.Append (" = ").Append (val.ToString ());
4655                         else if (val is IFormattable) {
4656                                 string value = ((IFormattable)val).ToString();
4657                                 if (val is string)
4658                                         value = "\"" + value + "\"";
4659                                 buf.Append (" = ").Append (value);
4660                         }
4661                 }
4662                 return buf;
4663         }
4664
4665         protected override string GetEventDeclaration (EventDefinition e)
4666         {
4667                 StringBuilder buf = new StringBuilder ();
4668                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4669                         return null;
4670                 }
4671
4672                 AppendModifiers (buf, e.AddMethod);
4673
4674                 buf.Append (" event ");
4675                 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4676                 buf.Append (e.Name).Append (';');
4677
4678                 return buf.ToString ();
4679         }
4680 }
4681
4682 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4683         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4684         {
4685                 return buf;
4686         }
4687 }
4688
4689 class DocTypeFullMemberFormatter : MemberFormatter {
4690         public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4691
4692         protected override char NestedTypeSeparator {
4693                 get {return '+';}
4694         }
4695 }
4696
4697 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4698         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4699         {
4700                 return buf;
4701         }
4702 }
4703
4704 class SlashDocMemberFormatter : MemberFormatter {
4705
4706         protected override char[] GenericTypeContainer {
4707                 get {return new char[]{'{', '}'};}
4708         }
4709
4710         private bool AddTypeCount = true;
4711
4712         private TypeReference genDeclType;
4713         private MethodReference genDeclMethod;
4714
4715         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4716         {
4717                 if (type is GenericParameter) {
4718                         int l = buf.Length;
4719                         if (genDeclType != null) {
4720                                 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4721                                 for (int i = 0; i < genArgs.Count; ++i) {
4722                                         if (genArgs [i].Name == type.Name) {
4723                                                 buf.Append ('`').Append (i);
4724                                                 break;
4725                                         }
4726                                 }
4727                         }
4728                         if (genDeclMethod != null) {
4729                                 IList<GenericParameter> genArgs = null;
4730                                 if (genDeclMethod.IsGenericMethod ()) {
4731                                         genArgs = genDeclMethod.GenericParameters;
4732                                         for (int i = 0; i < genArgs.Count; ++i) {
4733                                                 if (genArgs [i].Name == type.Name) {
4734                                                         buf.Append ("``").Append (i);
4735                                                         break;
4736                                                 }
4737                                         }
4738                                 }
4739                         }
4740                         if (genDeclType == null && genDeclMethod == null) {
4741                                 // Probably from within an explicitly implemented interface member,
4742                                 // where CSC uses parameter names instead of indices (why?), e.g.
4743                                 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4744                                 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4745                                 buf.Append (type.Name);
4746                         }
4747                         if (buf.Length == l) {
4748                                 throw new Exception (string.Format (
4749                                                 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}", 
4750                                                 type.Name, genDeclType, genDeclMethod));
4751                         }
4752                 }
4753                 else {
4754                         base.AppendTypeName (buf, type, context);
4755                         if (AddTypeCount) {
4756                                 int numArgs = type.GenericParameters.Count;
4757                                 if (type.DeclaringType != null)
4758                                         numArgs -= type.GenericParameters.Count;
4759                                 if (numArgs > 0) {
4760                                         buf.Append ('`').Append (numArgs);
4761                                 }
4762                         }
4763                 }
4764                 return buf;
4765         }
4766
4767         protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4768         {
4769                 buf.Append (ArrayDelimeters [0]);
4770                 int rank = array.Rank;
4771                 if (rank > 1) {
4772                         buf.Append ("0:");
4773                         for (int i = 1; i < rank; ++i) {
4774                                 buf.Append (",0:");
4775                         }
4776                 }
4777                 return buf.Append (ArrayDelimeters [1]);
4778         }
4779
4780         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4781         {
4782                 if (!AddTypeCount)
4783                         base.AppendGenericType (buf, type, context);
4784                 else
4785                         AppendType (buf, type, context);
4786                 return buf;
4787         }
4788
4789         private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4790         {
4791                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4792                 bool insertNested = false;
4793                 int prevParamCount = 0;
4794                 foreach (var decl in decls) {
4795                         if (insertNested)
4796                                 buf.Append (NestedTypeSeparator);
4797                         insertNested = true;
4798                         base.AppendTypeName (buf, decl, context);
4799                         int argCount = DocUtils.GetGenericArgumentCount (decl);
4800                         int numArgs = argCount - prevParamCount;
4801                         prevParamCount = argCount;
4802                         if (numArgs > 0)
4803                                 buf.Append ('`').Append (numArgs);
4804                 }
4805                 return buf;
4806         }
4807
4808         public override string GetDeclaration (MemberReference member)
4809         {
4810                 TypeReference r = member as TypeReference;
4811                 if (r != null) {
4812                         return "T:" + GetTypeName (r);
4813                 }
4814                 return base.GetDeclaration (member);
4815         }
4816
4817         protected override string GetConstructorName (MethodReference constructor)
4818         {
4819                 return GetMethodDefinitionName (constructor, "#ctor");
4820         }
4821
4822         protected override string GetMethodName (MethodReference method)
4823         {
4824                 string name = null;
4825                 MethodDefinition methodDef = method as MethodDefinition;
4826                 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4827                         name = method.Name;
4828                 else {
4829                         TypeReference iface;
4830                         MethodReference ifaceMethod;
4831                         DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4832                         AddTypeCount = false;
4833                         name = GetTypeName (iface) + "." + ifaceMethod.Name;
4834                         AddTypeCount = true;
4835                 }
4836                 return GetMethodDefinitionName (method, name);
4837         }
4838
4839         private string GetMethodDefinitionName (MethodReference method, string name)
4840         {
4841                 StringBuilder buf = new StringBuilder ();
4842                 buf.Append (GetTypeName (method.DeclaringType));
4843                 buf.Append ('.');
4844                 buf.Append (name.Replace (".", "#"));
4845                 if (method.IsGenericMethod ()) {
4846                         IList<GenericParameter> genArgs = method.GenericParameters;
4847                         if (genArgs.Count > 0)
4848                                 buf.Append ("``").Append (genArgs.Count);
4849                 }
4850                 IList<ParameterDefinition> parameters = method.Parameters;
4851                 try {
4852                         genDeclType   = method.DeclaringType;
4853                         genDeclMethod = method;
4854                         AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4855                 }
4856                 finally {
4857                         genDeclType   = null;
4858                         genDeclMethod = null;
4859                 }
4860                 return buf.ToString ();
4861         }
4862
4863         private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4864         {
4865                 if (parameters.Count == 0)
4866                         return buf;
4867
4868                 buf.Append ('(');
4869
4870                 AppendParameter (buf, genArgs, parameters [0]);
4871                 for (int i = 1; i < parameters.Count; ++i) {
4872                         buf.Append (',');
4873                         AppendParameter (buf, genArgs, parameters [i]);
4874                 }
4875
4876                 return buf.Append (')');
4877         }
4878
4879         private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4880         {
4881                 AddTypeCount = false;
4882                 buf.Append (GetTypeName (parameter.ParameterType));
4883                 AddTypeCount = true;
4884                 return buf;
4885         }
4886
4887         protected override string GetPropertyName (PropertyReference property)
4888         {
4889                 string name = null;
4890
4891                 PropertyDefinition propertyDef = property as PropertyDefinition;
4892                 MethodDefinition method = null;
4893                 if (propertyDef != null)
4894                         method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4895                 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4896                         name = property.Name;
4897                 else {
4898                         TypeReference iface;
4899                         MethodReference ifaceMethod;
4900                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4901                         AddTypeCount = false;
4902                         name = string.Join ("#", new string[]{
4903                                         GetTypeName (iface).Replace (".", "#"),
4904                                         DocUtils.GetMember (property.Name)
4905                         });
4906                         AddTypeCount = true;
4907                 }
4908
4909                 StringBuilder buf = new StringBuilder ();
4910                 buf.Append (GetName (property.DeclaringType));
4911                 buf.Append ('.');
4912                 buf.Append (name);
4913                 IList<ParameterDefinition> parameters = property.Parameters;
4914                 if (parameters.Count > 0) {
4915                         genDeclType = property.DeclaringType;
4916                         buf.Append ('(');
4917                         IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4918                         AppendParameter (buf, genArgs, parameters [0]);
4919                         for (int i = 1; i < parameters.Count; ++i) {
4920                                  buf.Append (',');
4921                                  AppendParameter (buf, genArgs, parameters [i]);
4922                         }
4923                         buf.Append (')');
4924                         genDeclType = null;
4925                 }
4926                 return buf.ToString ();
4927         }
4928
4929         protected override string GetFieldName (FieldReference field)
4930         {
4931                 return string.Format ("{0}.{1}",
4932                         GetName (field.DeclaringType), field.Name);
4933         }
4934
4935         protected override string GetEventName (EventReference e)
4936         {
4937                 return string.Format ("{0}.{1}",
4938                         GetName (e.DeclaringType), e.Name);
4939         }
4940
4941         protected override string GetTypeDeclaration (TypeDefinition type)
4942         {
4943                 string name = GetName (type);
4944                 if (type == null)
4945                         return null;
4946                 return "T:" + name;
4947         }
4948
4949         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4950         {
4951                 string name = GetName (constructor);
4952                 if (name == null)
4953                         return null;
4954                 return "M:" + name;
4955         }
4956
4957         protected override string GetMethodDeclaration (MethodDefinition method)
4958         {
4959                 string name = GetName (method);
4960                 if (name == null)
4961                         return null;
4962                 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4963                         genDeclType = method.DeclaringType;
4964                         genDeclMethod = method;
4965                         name += "~" + GetName (method.ReturnType);
4966                         genDeclType = null;
4967                         genDeclMethod = null;
4968                 }
4969                 return "M:" + name;
4970         }
4971
4972         protected override string GetPropertyDeclaration (PropertyDefinition property)
4973         {
4974                 string name = GetName (property);
4975                 if (name == null)
4976                         return null;
4977                 return "P:" + name;
4978         }
4979
4980         protected override string GetFieldDeclaration (FieldDefinition field)
4981         {
4982                 string name = GetName (field);
4983                 if (name == null)
4984                         return null;
4985                 return "F:" + name;
4986         }
4987
4988         protected override string GetEventDeclaration (EventDefinition e)
4989         {
4990                 string name = GetName (e);
4991                 if (name == null)
4992                         return null;
4993                 return "E:" + name;
4994         }
4995 }
4996
4997 class FileNameMemberFormatter : SlashDocMemberFormatter {
4998         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4999         {
5000                 return buf;
5001         }
5002
5003         protected override char NestedTypeSeparator {
5004                 get {return '+';}
5005         }
5006 }
5007
5008 }