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