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