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