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