Add autoconf checks for platforms without IPv6
[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 TypeReference GetUnderlyingType (this TypeDefinition type)
3011         {
3012                 if (!type.IsEnum)
3013                         return type;
3014                 return type.Fields.First (f => f.Name == "value__").FieldType;
3015         }
3016
3017         public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3018         {
3019                 return self.Types.SelectMany (t => t.GetAllTypes ());
3020         }
3021
3022         static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3023         {
3024                 yield return self;
3025
3026                 if (!self.HasNestedTypes)
3027                         yield break;
3028
3029                 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3030                         yield return type;
3031         }
3032 }
3033
3034 enum ApiStyle {
3035         Classic,
3036         Unified
3037 }
3038
3039 static class DocUtils {
3040
3041         public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3042                 string styleString = style.ToString ().ToLowerInvariant ();
3043                         string apistylevalue = element.GetAttribute ("apistyle");
3044                         return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3045         }
3046         public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3047                 string styleString = style.ToString ().ToLowerInvariant ();
3048                 return element.GetAttribute ("apistyle") == styleString;
3049         }
3050         public static bool HasApiStyle(this XmlNode node, ApiStyle style) 
3051         {
3052                 var attribute = node.Attributes ["apistyle"];
3053                 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3054         }
3055         public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3056                 string styleString = style.ToString ().ToLowerInvariant ();
3057                 var existingValue = element.GetAttribute ("apistyle");
3058                 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3059                         element.SetAttribute ("apistyle", styleString);
3060                 }
3061                 
3062                 // Propagate the API style up to the membernode if necessary
3063                 if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
3064                         var member = element.ParentNode;
3065                         var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
3066                         var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
3067
3068                         var parentAttribute = element.ParentNode.Attributes ["apistyle"];
3069                         Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
3070                         Action propagateStyle = () => {
3071                                 if (parentAttribute == null) {
3072                                         // if it doesn't have the attribute, then add it
3073                                         parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
3074                                         parentAttribute.Value = styleString;
3075                                         element.ParentNode.Attributes.Append (parentAttribute);
3076                                 } 
3077                         };
3078
3079                         if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null)) 
3080                                 removeStyle ();
3081                         else
3082                                 propagateStyle ();
3083                 }
3084         }
3085         public static void AddApiStyle (this XmlNode node, ApiStyle style) 
3086         {
3087                 string styleString = style.ToString ().ToLowerInvariant ();
3088                 var existingAttribute = node.Attributes ["apistyle"];
3089                 if (existingAttribute == null) {
3090                         existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
3091                         node.Attributes.Append (existingAttribute);
3092                 }
3093                 existingAttribute.Value = styleString;
3094         }
3095         public static void RemoveApiStyle (this XmlElement element, ApiStyle style) 
3096         {
3097                 string styleString = style.ToString ().ToLowerInvariant ();
3098                 string existingValue = element.GetAttribute ("apistyle");
3099                 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3100                         element.RemoveAttribute ("apistyle");
3101                 }
3102         }
3103         public static void RemoveApiStyle (this XmlNode node, ApiStyle style) 
3104         {
3105                 var styleAttribute = node.Attributes ["apistyle"];
3106                 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3107                         node.Attributes.Remove (styleAttribute);
3108                 }
3109         }
3110
3111         public static bool IsExplicitlyImplemented (MethodDefinition method)
3112         {
3113                 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3114         }
3115
3116         public static string GetTypeDotMember (string name)
3117         {
3118                 int startType, startMethod;
3119                 startType = startMethod = -1;
3120                 for (int i = 0; i < name.Length; ++i) {
3121                         if (name [i] == '.') {
3122                                 startType = startMethod;
3123                                 startMethod = i;
3124                         }
3125                 }
3126                 return name.Substring (startType+1);
3127         }
3128
3129         public static string GetMember (string name)
3130         {
3131                 int i = name.LastIndexOf ('.');
3132                 if (i == -1)
3133                         return name;
3134                 return name.Substring (i+1);
3135         }
3136
3137         public static void GetInfoForExplicitlyImplementedMethod (
3138                         MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3139         {
3140                 iface = null;
3141                 ifaceMethod = null;
3142                 if (method.Overrides.Count != 1)
3143                         throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3144                 iface = method.Overrides [0].DeclaringType;
3145                 ifaceMethod = method.Overrides [0];
3146         }
3147
3148         public static string GetPropertyName (PropertyDefinition pi)
3149         {
3150                 // Issue: (g)mcs-generated assemblies that explicitly implement
3151                 // properties don't specify the full namespace, just the 
3152                 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3153                 MethodDefinition method = pi.GetMethod;
3154                 if (method == null)
3155                         method = pi.SetMethod;
3156                 if (!IsExplicitlyImplemented (method))
3157                         return pi.Name;
3158
3159                 // Need to determine appropriate namespace for this member.
3160                 TypeReference iface;
3161                 MethodReference ifaceMethod;
3162                 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3163                 return string.Join (".", new string[]{
3164                                 DocTypeFullMemberFormatter.Default.GetName (iface),
3165                                 GetMember (pi.Name)});
3166         }
3167
3168         public static string GetNamespace (TypeReference type)
3169         {
3170                 if (type.GetElementType ().IsNested)
3171                         type = type.GetElementType ();
3172                 while (type != null && type.IsNested)
3173                         type = type.DeclaringType;
3174                 if (type == null)
3175                         return string.Empty;
3176
3177                         string typeNS = type.Namespace;
3178
3179                         // first, make sure this isn't a type reference to another assembly/module
3180
3181                         bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3182                         if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3183                                 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3184                         }
3185                         return typeNS;
3186         }
3187
3188         public static string PathCombine (string dir, string path)
3189         {
3190                 if (dir == null)
3191                         dir = "";
3192                 if (path == null)
3193                         path = "";
3194                 return Path.Combine (dir, path);
3195         }
3196
3197         public static bool IsExtensionMethod (MethodDefinition method)
3198         {
3199                 return
3200                         method.CustomAttributes
3201                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3202                         && method.DeclaringType.CustomAttributes
3203                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3204         }
3205
3206         public static bool IsDelegate (TypeDefinition type)
3207         {
3208                 TypeReference baseRef = type.BaseType;
3209                 if (baseRef == null)
3210                         return false;
3211                 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3212                                 baseRef.FullName == "System.MulticastDelegate";
3213         }
3214
3215         public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3216         {
3217                 List<TypeReference> decls = new List<TypeReference> ();
3218                 decls.Add (type);
3219                 while (type.DeclaringType != null) {
3220                         decls.Add (type.DeclaringType);
3221                         type = type.DeclaringType;
3222                 }
3223                 decls.Reverse ();
3224                 return decls;
3225         }
3226
3227         public static int GetGenericArgumentCount (TypeReference type)
3228         {
3229                 GenericInstanceType inst = type as GenericInstanceType;
3230                 return inst != null
3231                                 ? inst.GenericArguments.Count
3232                                 : type.GenericParameters.Count;
3233         }
3234
3235         public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3236         {
3237                 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3238                 List<TypeReference> userInterfaces = new List<TypeReference> ();
3239                 foreach (var ii in type.Interfaces) {
3240                         var iface = ii.InterfaceType;
3241                         TypeReference lookup = iface.Resolve () ?? iface;
3242                         if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3243                                 userInterfaces.Add (iface);
3244                 }
3245                 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3246         }
3247
3248         private static string GetQualifiedTypeName (TypeReference type)
3249         {
3250                 return "[" + type.Scope.Name + "]" + type.FullName;
3251         }
3252
3253         private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3254         {
3255                 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3256                 Action<TypeDefinition> a = null;
3257                 a = t => {
3258                         if (t == null) return;
3259                         foreach (var r in t.Interfaces) {
3260                                 inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
3261                                 a (r.InterfaceType.Resolve ());
3262                         }
3263                 };
3264                 TypeReference baseRef = type.BaseType;
3265                 while (baseRef != null) {
3266                         TypeDefinition baseDef = baseRef.Resolve ();
3267                         if (baseDef != null) {
3268                                 a (baseDef);
3269                                 baseRef = baseDef.BaseType;
3270                         }
3271                         else
3272                                 baseRef = null;
3273                 }
3274                 foreach (var r in type.Interfaces)
3275                         a (r.InterfaceType.Resolve ());
3276                 return inheritedInterfaces;
3277         }
3278 }
3279
3280 class DocsNodeInfo {
3281         public DocsNodeInfo (XmlElement node)
3282         {
3283                 this.Node = node;
3284         }
3285
3286         public DocsNodeInfo (XmlElement node, TypeDefinition type)
3287                 : this (node)
3288         {
3289                 SetType (type);
3290         }
3291
3292         public DocsNodeInfo (XmlElement node, MemberReference member)
3293                 : this (node)
3294         {
3295                 SetMemberInfo (member);
3296         }
3297
3298         void SetType (TypeDefinition type)
3299         {
3300                 if (type == null)
3301                         throw new ArgumentNullException ("type");
3302                 Type = type;
3303                 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3304                 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3305                 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3306                 for (int i = 0; i < declTypes.Count - 1; ++i) {
3307                         int remove = System.Math.Min (maxGenArgs, 
3308                                         DocUtils.GetGenericArgumentCount (declTypes [i]));
3309                         maxGenArgs -= remove;
3310                         while (remove-- > 0)
3311                                 GenericParameters.RemoveAt (0);
3312                 }
3313                 if (DocUtils.IsDelegate (type)) {
3314                         Parameters = type.GetMethod("Invoke").Parameters;
3315                         ReturnType = type.GetMethod("Invoke").ReturnType;
3316                         ReturnIsReturn = true;
3317                 }
3318         }
3319
3320         void SetMemberInfo (MemberReference member)
3321         {
3322                 if (member == null)
3323                         throw new ArgumentNullException ("member");
3324                 ReturnIsReturn = true;
3325                 AddRemarks = true;
3326                 Member = member;
3327                 
3328                 if (member is MethodReference ) {
3329                         MethodReference mr = (MethodReference) member;
3330                         Parameters = mr.Parameters;
3331                         if (mr.IsGenericMethod ()) {
3332                                 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3333                         }
3334                 }
3335                 else if (member is PropertyDefinition) {
3336                         Parameters = ((PropertyDefinition) member).Parameters;
3337                 }
3338                         
3339                 if (member is MethodDefinition) {
3340                         ReturnType = ((MethodDefinition) member).ReturnType;
3341                 } else if (member is PropertyDefinition) {
3342                         ReturnType = ((PropertyDefinition) member).PropertyType;
3343                         ReturnIsReturn = false;
3344                 }
3345
3346                 // no remarks section for enum members
3347                 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3348                         AddRemarks = false;
3349         }
3350
3351         public TypeReference ReturnType;
3352         public List<GenericParameter> GenericParameters;
3353         public IList<ParameterDefinition> Parameters;
3354         public bool ReturnIsReturn;
3355         public XmlElement Node;
3356         public bool AddRemarks = true;
3357         public MemberReference Member;
3358         public TypeDefinition Type;
3359
3360         public override string ToString ()
3361         {
3362                 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3363         }
3364 }
3365
3366 class DocumentationEnumerator {
3367         
3368         public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3369         {
3370                 return GetDocumentationTypes (assembly, forTypes, null);
3371         }
3372
3373         protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3374         {
3375                 foreach (TypeDefinition type in assembly.GetTypes()) {
3376                         if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3377                                 continue;
3378                         if (seen != null && seen.Contains (type.FullName))
3379                                 continue;
3380                         yield return type;
3381                         foreach (TypeDefinition nested in type.NestedTypes)
3382                                 yield return nested;
3383                 }
3384         }
3385
3386         public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3387         {
3388                 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3389                         if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3390                                 oldmember.RemoveAttribute ("__monodocer-seen__");
3391                                 continue;
3392                         }
3393                         MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3394                         if (m == null) {
3395                                 yield return new DocsNodeInfo (oldmember);
3396                         }
3397                         else {
3398                                 yield return new DocsNodeInfo (oldmember, m);
3399                         }
3400                 }
3401         }
3402
3403         protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3404         {
3405                 string membertype = member.MemberType;
3406                 
3407                 string returntype = member.ReturnType;
3408                 
3409                 string docName = member.MemberName;
3410
3411                 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3412
3413                 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3414                 MemberReference likelyCandidate = null;
3415                 
3416                 // Loop through all members in this type with the same name
3417                 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3418                 foreach (MemberReference mi in reflectedMembers) {
3419                         bool matchedMagicType = false;
3420                         if (mi is TypeDefinition) continue;
3421                         if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3422
3423                         if (MDocUpdater.IsPrivate (mi))
3424                                 continue;
3425
3426                         IList<ParameterDefinition> pis = null;
3427                         string[] typeParams = null;
3428                         if (mi is MethodDefinition) {
3429                                 MethodDefinition mb = (MethodDefinition) mi;
3430                                 pis = mb.Parameters;
3431                                 if (mb.IsGenericMethod ()) {
3432                                         IList<GenericParameter> args = mb.GenericParameters;
3433                                         typeParams = args.Select (p => p.Name).ToArray ();
3434                                 }
3435                         }
3436                         else if (mi is PropertyDefinition)
3437                                 pis = ((PropertyDefinition)mi).Parameters;
3438                                 
3439                         // check type parameters
3440                         int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3441                         int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3442                         if (methodTcount != reflectionTcount) 
3443                                 continue;
3444
3445                         // check member parameters
3446                         int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3447                         int pcount = pis == null ? 0 : pis.Count;
3448                         if (mcount != pcount)
3449                                 continue;
3450
3451                         MethodDefinition mDef = mi as MethodDefinition;
3452                         if (mDef != null && !mDef.IsConstructor) {
3453                                 // Casting operators can overload based on return type.
3454                                 string rtype = GetReplacedString (
3455                                                        MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType), 
3456                                                        typeParams, docTypeParams);
3457                                 string originalRType = rtype;
3458                                 if (MDocUpdater.SwitchingToMagicTypes) {
3459                                         rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3460                                         
3461                                 }
3462                                 if ((returntype != rtype && originalRType == rtype) ||
3463                                         (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3464                                         continue;
3465                                 }
3466
3467                                 if (originalRType != rtype)
3468                                         matchedMagicType = true;
3469                         }
3470
3471                         if (pcount == 0)
3472                                 return mi;
3473                         bool good = true;
3474                         for (int i = 0; i < pis.Count; i++) {
3475                                 string paramType = GetReplacedString (
3476                                         MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3477                                         typeParams, docTypeParams);
3478
3479                                 // if magictypes, replace paramType to "classic value" ... so the comparison works
3480                                 string originalParamType = paramType;
3481                                 if (MDocUpdater.SwitchingToMagicTypes) {
3482                                         paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3483                                 }
3484
3485                                 string xmlMemberType = member.Parameters [i];
3486                                 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) || 
3487                                         (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3488
3489                                         // did not match ... if we're dropping the namespace, and the paramType has the dropped
3490                                         // namespace, we should see if it matches when added
3491                                         bool stillDoesntMatch = true;
3492                                         if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3493                                                 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3494
3495                                                 stillDoesntMatch = withDroppedNs != paramType;
3496                                         }
3497
3498                                         if (stillDoesntMatch) {
3499                                                 good = false;
3500                                                 break;
3501                                         }
3502                                 }
3503
3504                                 if (originalParamType != paramType)
3505                                         matchedMagicType = true;
3506                         }
3507                         if (!good) continue;
3508
3509                         if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3510                                 // 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
3511                                 likelyCandidate = mi;
3512                                 continue;
3513                         }
3514
3515                         return mi;
3516                 }
3517                 
3518                 return likelyCandidate;
3519         }
3520
3521         static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3522         {
3523                 if (docName [docName.Length-1] != '>')
3524                         return null;
3525                 StringList types = new StringList ();
3526                 int endToken = docName.Length-2;
3527                 int i = docName.Length-2;
3528                 do {
3529                         if (docName [i] == ',' || docName [i] == '<') {
3530                                 types.Add (docName.Substring (i + 1, endToken - i));
3531                                 endToken = i-1;
3532                         }
3533                         if (docName [i] == '<')
3534                                 break;
3535                 } while (--i >= 0);
3536
3537                 types.Reverse ();
3538                 var arrayTypes = types.ToArray ();
3539
3540                 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3541                         return knownParameters.ToArray ();
3542                 else
3543                         return arrayTypes;
3544         }
3545
3546         protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3547         {
3548                 // In case of dropping the namespace, we have to remove the dropped NS
3549                 // so that docName will match what's in the assembly/type
3550                 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3551                         int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3552                         docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3553                 }
3554
3555                 // need to worry about 4 forms of //@MemberName values:
3556                 //  1. "Normal" (non-generic) member names: GetEnumerator
3557                 //    - Lookup as-is.
3558                 //  2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3559                 //    - try as-is, and try type.member (due to "kludge" for property
3560                 //      support.
3561                 //  3. "Normal" Generic member names: Sort<T> (CSC)
3562                 //    - need to remove generic parameters --> "Sort"
3563                 //  4. Explicitly-implemented interface members for generic interfaces: 
3564                 //    -- System.Collections.Generic.IEnumerable<T>.Current
3565                 //    - Try as-is, and try type.member, *keeping* the generic parameters.
3566                 //     --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3567                 //  5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3568                 //    'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3569                 //    this as (1) or (2).
3570                 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3571                         // Cases 1 & 2
3572                         foreach (MemberReference mi in type.GetMembers (docName))
3573                                 yield return mi;
3574                         if (CountChars (docName, '.') > 0)
3575                                 // might be a property; try only type.member instead of
3576                                 // namespace.type.member.
3577                                 foreach (MemberReference mi in 
3578                                                 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3579                                         yield return mi;
3580                         yield break;
3581                 }
3582                 // cases 3 & 4
3583                 int numLt = 0;
3584                 int numDot = 0;
3585                 int startLt, startType, startMethod;
3586                 startLt = startType = startMethod = -1;
3587                 for (int i = 0; i < docName.Length; ++i) {
3588                         switch (docName [i]) {
3589                                 case '<':
3590                                         if (numLt == 0) {
3591                                                 startLt = i;
3592                                         }
3593                                         ++numLt;
3594                                         break;
3595                                 case '>':
3596                                         --numLt;
3597                                         if (numLt == 0 && (i + 1) < docName.Length)
3598                                                 // there's another character in docName, so this <...> sequence is
3599                                                 // probably part of a generic type -- case 4.
3600                                                 startLt = -1;
3601                                         break;
3602                                 case '.':
3603                                         startType = startMethod;
3604                                         startMethod = i;
3605                                         ++numDot;
3606                                         break;
3607                         }
3608                 }
3609                 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3610                 // case 3
3611                 foreach (MemberReference mi in type.GetMembers (refName))
3612                         yield return mi;
3613
3614                 // case 4
3615                 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3616                         yield return mi;
3617
3618                 // If we _still_ haven't found it, we've hit another generic naming issue:
3619                 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3620                 // explicitly-implemented METHOD names (not properties), e.g. 
3621                 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3622                 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3623                 // which the XML docs will contain.
3624                 //
3625                 // Alas, we can't derive the Mono name from docName, so we need to iterate
3626                 // over all member names, convert them into CSC format, and compare... :-(
3627                 if (numDot == 0)
3628                         yield break;
3629                 foreach (MemberReference mi in type.GetMembers ()) {
3630                         if (MDocUpdater.GetMemberName (mi) == docName)
3631                                 yield return mi;
3632                 }
3633         }
3634
3635         static string GetReplacedString (string typeName, string[] from, string[] to)
3636         {
3637                 if (from == null)
3638                         return typeName;
3639                 for (int i = 0; i < from.Length; ++i)
3640                         typeName = typeName.Replace (from [i], to [i]);
3641                 return typeName;
3642         }
3643
3644         private static int CountChars (string s, char c)
3645         {
3646                 int count = 0;
3647                 for (int i = 0; i < s.Length; ++i) {
3648                         if (s [i] == c)
3649                                 ++count;
3650                 }
3651                 return count;
3652         }
3653 }
3654
3655 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3656
3657         XmlReader ecmadocs;
3658         MDocUpdater app;
3659
3660         public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3661         {
3662                 this.app      = app;
3663                 this.ecmadocs = ecmaDocs;
3664         }
3665
3666         public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3667         {
3668                 HashSet<string> seen = new HashSet<string> ();
3669                 return GetDocumentationTypes (assembly, forTypes, seen)
3670                         .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3671         }
3672
3673         new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3674         {
3675                 int typeDepth = -1;
3676                 while (ecmadocs.Read ()) {
3677                         switch (ecmadocs.Name) {
3678                                 case "Type": {
3679                                         if (typeDepth == -1)
3680                                                 typeDepth = ecmadocs.Depth;
3681                                         if (ecmadocs.NodeType != XmlNodeType.Element)
3682                                                 continue;
3683                                         if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3684                                                 continue;
3685                                         string typename = ecmadocs.GetAttribute ("FullName");
3686                                         string typename2 = MDocUpdater.GetTypeFileName (typename);
3687                                         if (forTypes != null && 
3688                                                         forTypes.BinarySearch (typename) < 0 &&
3689                                                         typename != typename2 &&
3690                                                         forTypes.BinarySearch (typename2) < 0)
3691                                                 continue;
3692                                         TypeDefinition t;
3693                                         if ((t = assembly.GetType (typename)) == null && 
3694                                                         (t = assembly.GetType (typename2)) == null)
3695                                                 continue;
3696                                         seen.Add (typename);
3697                                         if (typename != typename2)
3698                                                 seen.Add (typename2);
3699                                         Console.WriteLine ("  Import: {0}", t.FullName);
3700                                         if (ecmadocs.Name != "Docs") {
3701                                                 int depth = ecmadocs.Depth;
3702                                                 while (ecmadocs.Read ()) {
3703                                                         if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3704                                                                 break;
3705                                                 }
3706                                         }
3707                                         if (!ecmadocs.IsStartElement ("Docs"))
3708                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3709                                         yield return t;
3710                                         break;
3711                                 }
3712                                 default:
3713                                         break;
3714                         }
3715                 }
3716         }
3717
3718         public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3719         {
3720                 return GetMembers (basefile, type)
3721                         .Concat (base.GetDocumentationMembers (basefile, type));
3722         }
3723
3724         private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3725         {
3726                 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3727                         // do nothing
3728                 }
3729                 if (ecmadocs.IsEmptyElement)
3730                         yield break;
3731
3732                 int membersDepth = ecmadocs.Depth;
3733                 bool go = true;
3734                 while (go && ecmadocs.Read ()) {
3735                         switch (ecmadocs.Name) {
3736                                 case "Member": {
3737                                         if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3738                                                 continue;
3739                                         DocumentationMember dm = new DocumentationMember (ecmadocs);
3740                                         
3741                                         string xp = MDocUpdater.GetXPathForMember (dm);
3742                                         XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3743                                         MemberReference m;
3744                                         if (oldmember == null) {
3745                                                 m = GetMember (type, dm);
3746                                                 if (m == null) {
3747                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3748                                                                         type.FullName, dm.MemberSignatures ["C#"]);
3749                                                                         // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3750                                                         continue;
3751                                                 }
3752                                                 // oldmember lookup may have failed due to type parameter renames.
3753                                                 // Try again.
3754                                                 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3755                                                 if (oldmember == null) {
3756                                                         XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3757                                                         oldmember = basefile.CreateElement ("Member");
3758                                                         oldmember.SetAttribute ("MemberName", dm.MemberName);
3759                                                         members.AppendChild (oldmember);
3760                                                         foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3761                                                                 XmlElement ms = basefile.CreateElement ("MemberSignature");
3762                                                                 ms.SetAttribute ("Language", key);
3763                                                                 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3764                                                                 oldmember.AppendChild (ms);
3765                                                         }
3766                                                         oldmember.SetAttribute ("__monodocer-seen__", "true");
3767                                                         Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3768                                                         app.additions++;
3769                                                 }
3770                                         }
3771                                         else {
3772                                                 m = GetMember (type, new DocumentationMember (oldmember));
3773                                                 if (m == null) {
3774                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3775                                                                         type.FullName, dm.MemberSignatures ["C#"]);
3776                                                         continue;
3777                                                 }
3778                                                 oldmember.SetAttribute ("__monodocer-seen__", "true");
3779                                         }
3780                                         DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3781                                         if (ecmadocs.Name != "Docs")
3782                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3783                                         yield return node;
3784                                         break;
3785                                 }
3786                                 case "Members":
3787                                         if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3788                                                 go = false;
3789                                         }
3790                                         break;
3791                         }
3792                 }
3793         }
3794 }
3795
3796 abstract class DocumentationImporter {
3797
3798         public abstract void ImportDocumentation (DocsNodeInfo info);
3799 }
3800
3801 class MsxdocDocumentationImporter : DocumentationImporter {
3802
3803         XmlDocument slashdocs;
3804
3805         public MsxdocDocumentationImporter (string file)
3806         {
3807                 var xml = File.ReadAllText (file);
3808
3809                 // Ensure Unix line endings
3810                 xml = xml.Replace ("\r", "");
3811
3812                 slashdocs = new XmlDocument();
3813                 slashdocs.LoadXml (xml);
3814         }
3815
3816         public override void ImportDocumentation (DocsNodeInfo info)
3817         {
3818                 XmlNode elem = GetDocs (info.Member ?? info.Type);
3819
3820                 if (elem == null)
3821                         return;
3822
3823                 XmlElement e = info.Node;
3824
3825                 if (elem.SelectSingleNode("summary") != null)
3826                         MDocUpdater.ClearElement(e, "summary");
3827                 if (elem.SelectSingleNode("remarks") != null)
3828                         MDocUpdater.ClearElement(e, "remarks");
3829                 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3830                         MDocUpdater.ClearElement(e, "value");
3831                         MDocUpdater.ClearElement(e, "returns");
3832                 }
3833
3834                 foreach (XmlNode child in elem.ChildNodes) {
3835                         switch (child.Name) {
3836                                 case "param":
3837                                 case "typeparam": {
3838                                         XmlAttribute name = child.Attributes ["name"];
3839                                         if (name == null)
3840                                                 break;
3841                                         XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3842                                         if (p2 != null)
3843                                                 p2.InnerXml = child.InnerXml;
3844                                         break;
3845                                 }
3846                                 // Occasionally XML documentation will use <returns/> on
3847                                 // properties, so let's try to normalize things.
3848                                 case "value":
3849                                 case "returns": {
3850                                         XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3851                                         v.InnerXml = child.InnerXml;
3852                                         e.AppendChild (v);
3853                                         break;
3854                                 }
3855                                 case "altmember":
3856                                 case "exception":
3857                                 case "permission": {
3858                                         XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3859                                         if (cref == null)
3860                                                 break;
3861                                         XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3862                                         if (a == null) {
3863                                                 a = e.OwnerDocument.CreateElement (child.Name);
3864                                                 a.SetAttribute ("cref", cref.Value);
3865                                                 e.AppendChild (a);
3866                                         }
3867                                         a.InnerXml = child.InnerXml;
3868                                         break;
3869                                 }
3870                                 case "seealso": {
3871                                         XmlAttribute cref = child.Attributes ["cref"];
3872                                         if (cref == null)
3873                                                 break;
3874                                         XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3875                                         if (a == null) {
3876                                                 a = e.OwnerDocument.CreateElement ("altmember");
3877                                                 a.SetAttribute ("cref", cref.Value);
3878                                                 e.AppendChild (a);
3879                                         }
3880                                         break;
3881                                 }
3882                                 default: {
3883                                         bool add = true;
3884                                         if (child.NodeType == XmlNodeType.Element && 
3885                                                         e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3886                                                 add = false;
3887                                         if (add)
3888                                                 MDocUpdater.CopyNode (child, e);
3889                                         break;
3890                                 }
3891                         }
3892                 }
3893         }
3894
3895         private XmlNode GetDocs (MemberReference member)
3896         {
3897                 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3898                 if (slashdocsig != null)
3899                         return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3900                 return null;
3901         }
3902 }
3903
3904 class EcmaDocumentationImporter : DocumentationImporter {
3905
3906         XmlReader ecmadocs;
3907
3908         public EcmaDocumentationImporter (XmlReader ecmaDocs)
3909         {
3910                 this.ecmadocs = ecmaDocs;
3911         }
3912
3913         public override void ImportDocumentation (DocsNodeInfo info)
3914         {
3915                 if (!ecmadocs.IsStartElement ("Docs")) {
3916                         return;
3917                 }
3918
3919                 XmlElement e = info.Node;
3920
3921                 int depth = ecmadocs.Depth;
3922                 ecmadocs.ReadStartElement ("Docs");
3923                 while (ecmadocs.Read ()) {
3924                         if (ecmadocs.Name == "Docs") {
3925                                 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3926                                         break;
3927                                 else
3928                                         throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3929                         }
3930                         if (!ecmadocs.IsStartElement ())
3931                                 continue;
3932                         switch (ecmadocs.Name) {
3933                                 case "param":
3934                                 case "typeparam": {
3935                                         string name = ecmadocs.GetAttribute ("name");
3936                                         if (name == null)
3937                                                 break;
3938                                         XmlNode doc = e.SelectSingleNode (
3939                                                         ecmadocs.Name + "[@name='" + name + "']");
3940                                         string value = ecmadocs.ReadInnerXml ();
3941                                         if (doc != null)
3942                                                 doc.InnerXml = value.Replace ("\r", "");
3943                                         break;
3944                                 }
3945                                 case "altmember":
3946                                 case "exception":
3947                                 case "permission":
3948                                 case "seealso": {
3949                                         string name = ecmadocs.Name;
3950                                         string cref = ecmadocs.GetAttribute ("cref");
3951                                         if (cref == null)
3952                                                 break;
3953                                         XmlNode doc = e.SelectSingleNode (
3954                                                         ecmadocs.Name + "[@cref='" + cref + "']");
3955                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3956                                         if (doc != null)
3957                                                 doc.InnerXml = value;
3958                                         else {
3959                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3960                                                 n.SetAttribute ("cref", cref);
3961                                                 n.InnerXml = value;
3962                                                 e.AppendChild (n);
3963                                         }
3964                                         break;
3965                                 }
3966                                 default: {
3967                                         string name = ecmadocs.Name;
3968                                         string xpath = ecmadocs.Name;
3969                                         StringList attributes = new StringList (ecmadocs.AttributeCount);
3970                                         if (ecmadocs.MoveToFirstAttribute ()) {
3971                                                 do {
3972                                                         attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3973                                                 } while (ecmadocs.MoveToNextAttribute ());
3974                                                 ecmadocs.MoveToContent ();
3975                                         }
3976                                         if (attributes.Count > 0) {
3977                                                 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3978                                         }
3979                                         XmlNode doc = e.SelectSingleNode (xpath);
3980                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3981                                         if (doc != null) {
3982                                                 doc.InnerXml = value;
3983                                         }
3984                                         else {
3985                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3986                                                 n.InnerXml = value;
3987                                                 foreach (string a in attributes) {
3988                                                         int eq = a.IndexOf ('=');
3989                                                         n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3990                                                 }
3991                                                 e.AppendChild (n);
3992                                         }
3993                                         break;
3994                                 }
3995                         }
3996                 }
3997         }
3998 }
3999
4000 class DocumentationMember {
4001         public StringToStringMap MemberSignatures = new StringToStringMap ();
4002         public string ReturnType;
4003         public StringList Parameters;
4004         public StringList TypeParameters;
4005         public string MemberName;
4006         public string MemberType;
4007
4008         public DocumentationMember (XmlReader reader)
4009         {
4010                 MemberName = reader.GetAttribute ("MemberName");
4011                 int depth = reader.Depth;
4012                 bool go = true;
4013                 StringList p = new StringList ();
4014                 StringList tp = new StringList ();
4015                 do {
4016                         if (reader.NodeType != XmlNodeType.Element)
4017                                 continue;
4018
4019                         bool shouldUse = true;
4020                         try {
4021                                 string apistyle = reader.GetAttribute ("apistyle");
4022                                 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
4023                         }
4024                         catch (Exception ex) {}
4025                         switch (reader.Name) {
4026                                 case "MemberSignature":
4027                                         if (shouldUse) {
4028                                                 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
4029                                         }
4030                                         break;
4031                                 case "MemberType":
4032                                         MemberType = reader.ReadElementString ();
4033                                         break;
4034                                 case "ReturnType":
4035                                         if (reader.Depth == depth + 2 && shouldUse)
4036                                                 ReturnType = reader.ReadElementString ();
4037                                         break;
4038                                 case "Parameter":
4039                                         if (reader.Depth == depth + 2 && shouldUse)
4040                                                 p.Add (reader.GetAttribute ("Type"));
4041                                         break;
4042                                 case "TypeParameter":
4043                                         if (reader.Depth == depth + 2 && shouldUse)
4044                                                 tp.Add (reader.GetAttribute ("Name"));
4045                                         break;
4046                                 case "Docs":
4047                                         if (reader.Depth == depth + 1)
4048                                                 go = false;
4049                                         break;
4050                         }
4051                 } while (go && reader.Read () && reader.Depth >= depth);
4052                 if (p.Count > 0) {
4053                         Parameters = p;
4054                 }
4055                 if (tp.Count > 0) {
4056                         TypeParameters = tp;
4057                 } else {
4058                         DiscernTypeParameters ();
4059                 }
4060         }
4061
4062         public DocumentationMember (XmlNode node)
4063         {
4064                 MemberName = node.Attributes ["MemberName"].Value;
4065                 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4066                         XmlAttribute l = n.Attributes ["Language"];
4067                         XmlAttribute v = n.Attributes ["Value"];
4068                         XmlAttribute apistyle = n.Attributes ["apistyle"];
4069                         bool shouldUse = apistyle == null || apistyle.Value == "classic";
4070                         if (l != null && v != null && shouldUse)
4071                                 MemberSignatures [l.Value] = v.Value;
4072                 }
4073                 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4074                 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4075                 if (rt != null)
4076                         ReturnType = rt.InnerText;
4077                 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4078                 if (p.Count > 0) {
4079                         Parameters = new StringList (p.Count);
4080                         for (int i = 0; i < p.Count; ++i)
4081                                 Parameters.Add (p [i].Attributes ["Type"].Value);
4082                 }
4083                 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4084                 if (tp.Count > 0) {
4085                         TypeParameters = new StringList (tp.Count);
4086                         for (int i = 0; i < tp.Count; ++i)
4087                                 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4088                 }
4089                 else {
4090                         DiscernTypeParameters ();
4091                 }
4092         }
4093
4094         void DiscernTypeParameters ()
4095         {
4096                 // see if we can discern the param list from the name
4097                 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4098                         var starti = MemberName.IndexOf ("<") + 1;
4099                         var endi = MemberName.LastIndexOf (">");
4100                         var paramlist = MemberName.Substring (starti, endi - starti);
4101                         var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4102                         TypeParameters = new StringList (tparams);
4103                 }
4104         }
4105 }
4106
4107 public class DynamicParserContext {
4108         public ReadOnlyCollection<bool> TransformFlags;
4109         public int TransformIndex;
4110
4111         public DynamicParserContext (ICustomAttributeProvider provider)
4112         {
4113                 CustomAttribute da;
4114                 if (provider.HasCustomAttributes &&
4115                                 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4116                                         .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4117                         CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4118                                 ? new CustomAttributeArgument [0]
4119                                 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4120
4121                         TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4122                 }
4123         }
4124 }
4125
4126 public enum MemberFormatterState {
4127         None,
4128         WithinGenericTypeParameters,
4129 }
4130
4131 public abstract class MemberFormatter {
4132
4133         public virtual string Language {
4134                 get {return "";}
4135         }
4136
4137         public string GetName (MemberReference member)
4138         {
4139                 return GetName (member, null);
4140         }
4141
4142         public virtual string GetName (MemberReference member, DynamicParserContext context)
4143         {
4144                 TypeReference type = member as TypeReference;
4145                 if (type != null)
4146                         return GetTypeName (type, context);
4147                 MethodReference method  = member as MethodReference;
4148                 if (method != null && method.Name == ".ctor") // method.IsConstructor
4149                         return GetConstructorName (method);
4150                 if (method != null)
4151                         return GetMethodName (method);
4152                 PropertyReference prop = member as PropertyReference;
4153                 if (prop != null)
4154                         return GetPropertyName (prop);
4155                 FieldReference field = member as FieldReference;
4156                 if (field != null)
4157                         return GetFieldName (field);
4158                 EventReference e = member as EventReference;
4159                 if (e != null)
4160                         return GetEventName (e);
4161                 throw new NotSupportedException ("Can't handle: " +
4162                                         (member == null ? "null" : member.GetType().ToString()));
4163         }
4164
4165         protected virtual string GetTypeName (TypeReference type)
4166         {
4167                 return GetTypeName (type, null);
4168         }
4169
4170         protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4171         {
4172                 if (type == null)
4173                         throw new ArgumentNullException ("type");
4174                 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4175         }
4176
4177         protected virtual char[] ArrayDelimeters {
4178                 get {return new char[]{'[', ']'};}
4179         }
4180
4181         protected virtual MemberFormatterState MemberFormatterState { get; set; }
4182
4183         protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4184         {
4185                 if (type is ArrayType) {
4186                         TypeSpecification spec = type as TypeSpecification;
4187                         _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4188                         return AppendArrayModifiers (buf, (ArrayType) type);
4189                 }
4190                 if (type is ByReferenceType) {
4191                         return AppendRefTypeName (buf, type, context);
4192                 }
4193                 if (type is PointerType) {
4194                         return AppendPointerTypeName (buf, type, context);
4195                 }
4196                 if (type is GenericParameter) {
4197                         return AppendTypeName (buf, type, context);
4198                 }
4199                 AppendNamespace (buf, type);
4200                 GenericInstanceType genInst = type as GenericInstanceType;
4201                 if (type.GenericParameters.Count == 0 &&
4202                                 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4203                         return AppendFullTypeName (buf, type, context);
4204                 }
4205                 return AppendGenericType (buf, type, context);
4206         }
4207
4208         protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4209         {
4210                 string ns = DocUtils.GetNamespace (type);
4211                 if (ns != null && ns.Length > 0)
4212                         buf.Append (ns).Append ('.');
4213                 return buf;
4214         }
4215
4216         protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4217         {
4218                 if (type.DeclaringType != null)
4219                         AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4220                 return AppendTypeName (buf, type, context);
4221         }
4222
4223         protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4224         {
4225                 if (context != null)
4226                         context.TransformIndex++;
4227                 return AppendTypeName (buf, type.Name);
4228         }
4229
4230         protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4231         {
4232                 int n = typename.IndexOf ("`");
4233                 if (n >= 0)
4234                         return buf.Append (typename.Substring (0, n));
4235                 return buf.Append (typename);
4236         }
4237
4238         protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4239         {
4240                 buf.Append (ArrayDelimeters [0]);
4241                 int rank = array.Rank;
4242                 if (rank > 1)
4243                         buf.Append (new string (',', rank-1));
4244                 return buf.Append (ArrayDelimeters [1]);
4245         }
4246
4247         protected virtual string RefTypeModifier {
4248                 get {return "@";}
4249         }
4250
4251         protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4252         {
4253                 TypeSpecification spec = type as TypeSpecification;
4254                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4255                                 .Append (RefTypeModifier);
4256         }
4257
4258         protected virtual string PointerModifier {
4259                 get {return "*";}
4260         }
4261
4262         protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4263         {
4264                 TypeSpecification spec = type as TypeSpecification;
4265                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4266                                 .Append (PointerModifier);
4267         }
4268
4269         protected virtual char[] GenericTypeContainer {
4270                 get {return new char[]{'<', '>'};}
4271         }
4272
4273         protected virtual char NestedTypeSeparator {
4274                 get {return '.';}
4275         }
4276
4277         protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4278         {
4279                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4280                                 type is GenericInstanceType ? type.GetElementType () : type);
4281                 List<TypeReference> genArgs = GetGenericArguments (type);
4282                 int argIdx = 0;
4283                 int prev = 0;
4284                 bool insertNested = false;
4285                 foreach (var decl in decls) {
4286                         TypeReference declDef = decl.Resolve () ?? decl;
4287                         if (insertNested) {
4288                                 buf.Append (NestedTypeSeparator);
4289                         }
4290                         insertNested = true;
4291                         AppendTypeName (buf, declDef, context);
4292                         int ac = DocUtils.GetGenericArgumentCount (declDef);
4293                         int c = ac - prev;
4294                         prev = ac;
4295                         if (c > 0) {
4296                                 buf.Append (GenericTypeContainer [0]);
4297                                 var origState = MemberFormatterState;
4298                                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4299                                 _AppendTypeName (buf, genArgs [argIdx++], context);
4300                                 for (int i = 1; i < c; ++i) {
4301                                         _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4302                                 }
4303                                 MemberFormatterState = origState;
4304                                 buf.Append (GenericTypeContainer [1]);
4305                         }
4306                 }
4307                 return buf;
4308         }
4309
4310         protected List<TypeReference> GetGenericArguments (TypeReference type)
4311         {
4312                 var args = new List<TypeReference> ();
4313                 GenericInstanceType inst = type as GenericInstanceType;
4314                 if (inst != null)
4315                         args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4316                 else
4317                         args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4318                 return args;
4319         }
4320
4321         protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4322         {
4323                 return buf;
4324         }
4325
4326         protected virtual string GetConstructorName (MethodReference constructor)
4327         {
4328                 return constructor.Name;
4329         }
4330
4331         protected virtual string GetMethodName (MethodReference method)
4332         {
4333                 return method.Name;
4334         }
4335
4336         protected virtual string GetPropertyName (PropertyReference property)
4337         {
4338                 return property.Name;
4339         }
4340
4341         protected virtual string GetFieldName (FieldReference field)
4342         {
4343                 return field.Name;
4344         }
4345
4346         protected virtual string GetEventName (EventReference e)
4347         {
4348                 return e.Name;
4349         }
4350         
4351         public string GetDeclaration (MemberReference mreference)
4352         {
4353                 return GetDeclaration (mreference.Resolve ());
4354         }
4355
4356         string GetDeclaration (IMemberDefinition member)
4357         {
4358                 if (member == null)
4359                         throw new ArgumentNullException ("member");
4360                 TypeDefinition type = member as TypeDefinition;
4361                 if (type != null)
4362                         return GetTypeDeclaration (type);
4363                 MethodDefinition method = member as MethodDefinition;
4364                 if (method != null && method.IsConstructor)
4365                         return GetConstructorDeclaration (method);
4366                 if (method != null)
4367                         return GetMethodDeclaration (method);
4368                 PropertyDefinition prop = member as PropertyDefinition;
4369                 if (prop != null)
4370                         return GetPropertyDeclaration (prop);
4371                 FieldDefinition field = member as FieldDefinition;
4372                 if (field != null)
4373                         return GetFieldDeclaration (field);
4374                 EventDefinition e = member as EventDefinition;
4375                 if (e != null)
4376                         return GetEventDeclaration (e);
4377                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4378         }
4379
4380         protected virtual string GetTypeDeclaration (TypeDefinition type)
4381         {
4382                 if (type == null)
4383                         throw new ArgumentNullException ("type");
4384                 StringBuilder buf = new StringBuilder (type.Name.Length);
4385                 _AppendTypeName (buf, type, null);
4386                 AppendGenericTypeConstraints (buf, type);
4387                 return buf.ToString ();
4388         }
4389
4390         protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4391         {
4392                 return GetConstructorName (constructor);
4393         }
4394
4395         protected virtual string GetMethodDeclaration (MethodDefinition method)
4396         {
4397                 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4398                                         ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4399                         return null;
4400
4401                 // Special signature for destructors.
4402                 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4403                         return GetFinalizerName (method);
4404
4405                 StringBuilder buf = new StringBuilder ();
4406
4407                 AppendVisibility (buf, method);
4408                 if (buf.Length == 0 && 
4409                                 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4410                         return null;
4411
4412                 AppendModifiers (buf, method);
4413
4414                 if (buf.Length != 0)
4415                         buf.Append (" ");
4416                 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4417
4418                 AppendMethodName (buf, method);
4419                 AppendGenericMethod (buf, method).Append (" ");
4420                 AppendParameters (buf, method, method.Parameters);
4421                 AppendGenericMethodConstraints (buf, method);
4422                 return buf.ToString ();
4423         }
4424
4425         protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4426         {
4427                 return buf.Append (method.Name);
4428         }
4429
4430         protected virtual string GetFinalizerName (MethodDefinition method)
4431         {
4432                 return "Finalize";
4433         }
4434
4435         protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4436         {
4437                 return buf;
4438         }
4439
4440         protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4441         {
4442                 return buf;
4443         }
4444
4445         protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4446         {
4447                 return buf;
4448         }
4449
4450         protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4451         {
4452                 return buf;
4453         }
4454
4455         protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4456         {
4457                 return buf;
4458         }
4459
4460         protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4461         {
4462                 return GetPropertyName (property);
4463         }
4464
4465         protected virtual string GetFieldDeclaration (FieldDefinition field)
4466         {
4467                 return GetFieldName (field);
4468         }
4469
4470         protected virtual string GetEventDeclaration (EventDefinition e)
4471         {
4472                 return GetEventName (e);
4473         }
4474 }
4475
4476 class ILFullMemberFormatter : MemberFormatter {
4477
4478         public override string Language {
4479                 get {return "ILAsm";}
4480         }
4481
4482         protected override char NestedTypeSeparator {
4483                 get {
4484                         return '/';
4485                 }
4486         }
4487
4488         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4489         {
4490                 if (GetBuiltinType (type.FullName) != null)
4491                         return buf;
4492                 string ns = DocUtils.GetNamespace (type);
4493                 if (ns != null && ns.Length > 0) {
4494                         if (type.IsValueType)
4495                                 buf.Append ("valuetype ");
4496                         else
4497                                 buf.Append ("class ");
4498                         buf.Append (ns).Append ('.');
4499                 }
4500                 return buf;
4501         }
4502
4503         protected static string GetBuiltinType (string t)
4504         {
4505                 switch (t) {
4506                 case "System.Byte":    return "unsigned int8";
4507                 case "System.SByte":   return "int8";
4508                 case "System.Int16":   return "int16";
4509                 case "System.Int32":   return "int32";
4510                 case "System.Int64":   return "int64";
4511                 case "System.IntPtr":  return "native int";
4512
4513                 case "System.UInt16":  return "unsigned int16";
4514                 case "System.UInt32":  return "unsigned int32";
4515                 case "System.UInt64":  return "unsigned int64";
4516                 case "System.UIntPtr": return "native unsigned int";
4517
4518                 case "System.Single":  return "float32";
4519                 case "System.Double":  return "float64";
4520                 case "System.Boolean": return "bool";
4521                 case "System.Char":    return "char";
4522                 case "System.Void":    return "void";
4523                 case "System.String":  return "string";
4524                 case "System.Object":  return "object";
4525                 }
4526                 return null;
4527         }
4528
4529         protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4530         {
4531                 return buf.Append (typename);
4532         }
4533
4534         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4535         {
4536                 if (type is GenericParameter) {
4537                         AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4538                         return buf;
4539                 }
4540
4541                 string s = GetBuiltinType (type.FullName);
4542                 if (s != null) {
4543                         return buf.Append (s);
4544                 }
4545                 return base.AppendTypeName (buf, type, context);
4546         }
4547
4548         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4549         {
4550                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4551                         return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4552                 }
4553                 GenericParameterAttributes attrs = type.Attributes;
4554                 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4555                         buf.Append ("class ");
4556                 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4557                         buf.Append ("struct ");
4558                 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4559                         buf.Append (".ctor ");
4560                 IList<TypeReference> constraints = type.Constraints;
4561                 MemberFormatterState = 0;
4562                 if (constraints.Count > 0) {
4563                         var full = new ILFullMemberFormatter ();
4564                         buf.Append ("(").Append (full.GetName (constraints [0]));
4565                         for (int i = 1; i < constraints.Count; ++i) {
4566                                 buf.Append (", ").Append (full.GetName (constraints [i]));
4567                         }
4568                         buf.Append (") ");
4569                 }
4570                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4571
4572                 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4573                         buf.Append ("+ ");
4574                 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4575                         buf.Append ("- ");
4576                 return buf;
4577         }
4578
4579         protected override string GetTypeDeclaration (TypeDefinition type)
4580         {
4581                 string visibility = GetTypeVisibility (type.Attributes);
4582                 if (visibility == null)
4583                         return null;
4584
4585                 StringBuilder buf = new StringBuilder ();
4586
4587                 buf.Append (".class ");
4588                 if (type.IsNested)
4589                         buf.Append ("nested ");
4590                 buf.Append (visibility).Append (" ");
4591                 if (type.IsInterface)
4592                         buf.Append ("interface ");
4593                 if (type.IsSequentialLayout)
4594                         buf.Append ("sequential ");
4595                 if (type.IsAutoLayout)
4596                         buf.Append ("auto ");
4597                 if (type.IsAnsiClass)
4598                         buf.Append ("ansi ");
4599                 if (type.IsAbstract)
4600                         buf.Append ("abstract ");
4601                 if (type.IsSerializable)
4602                         buf.Append ("serializable ");
4603                 if (type.IsSealed)
4604                         buf.Append ("sealed ");
4605                 if (type.IsBeforeFieldInit)
4606                         buf.Append ("beforefieldinit ");
4607                 var state = MemberFormatterState;
4608                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4609                 buf.Append (GetName (type));
4610                 MemberFormatterState = state;
4611                 var full = new ILFullMemberFormatter ();
4612                 if (type.BaseType != null) {
4613                         buf.Append (" extends ");
4614                         if (type.BaseType.FullName == "System.Object")
4615                                 buf.Append ("System.Object");
4616                         else
4617                                 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4618                 }
4619                 bool first = true;
4620                 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
4621                                 .Select (i => full.GetName (i.InterfaceType))
4622                                 .OrderBy (n => n)) {
4623                         if (first) {
4624                                 buf.Append (" implements ");
4625                                 first = false;
4626                         }
4627                         else {
4628                                 buf.Append (", ");
4629                         }
4630                         buf.Append (name);
4631                 }
4632
4633                 return buf.ToString ();
4634         }
4635
4636         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4637         {
4638                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4639                                 type is GenericInstanceType ? type.GetElementType () : type);
4640                 bool first = true;
4641                 foreach (var decl in decls) {
4642                         TypeReference declDef = decl.Resolve () ?? decl;
4643                         if (!first) {
4644                                 buf.Append (NestedTypeSeparator);
4645                         }
4646                         first = false;
4647                         AppendTypeName (buf, declDef, context);
4648                 }
4649                 buf.Append ('<');
4650                 first = true;
4651                 foreach (TypeReference arg in GetGenericArguments (type)) {
4652                         if (!first)
4653                                 buf.Append (", ");
4654                         first = false;
4655                         _AppendTypeName (buf, arg, context);
4656                 }
4657                 buf.Append ('>');
4658                 return buf;
4659         }
4660
4661         static string GetTypeVisibility (TypeAttributes ta)
4662         {
4663                 switch (ta & TypeAttributes.VisibilityMask) {
4664                 case TypeAttributes.Public:
4665                 case TypeAttributes.NestedPublic:
4666                         return "public";
4667
4668                 case TypeAttributes.NestedFamily:
4669                 case TypeAttributes.NestedFamORAssem:
4670                         return "protected";
4671
4672                 default:
4673                         return null;
4674                 }
4675         }
4676
4677         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4678         {
4679                 return GetMethodDeclaration (constructor);
4680         }
4681
4682         protected override string GetMethodDeclaration (MethodDefinition method)
4683         {
4684                 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4685                         return null;
4686
4687                 var buf = new StringBuilder ();
4688                 buf.Append (".method ");
4689                 AppendVisibility (buf, method);
4690                 if (method.IsStatic)
4691                         buf.Append ("static ");
4692                 if (method.IsHideBySig)
4693                         buf.Append ("hidebysig ");
4694                 if (method.IsPInvokeImpl) {
4695                         var info = method.PInvokeInfo;
4696                         buf.Append ("pinvokeimpl (\"")
4697                                 .Append (info.Module.Name)
4698                                 .Append ("\" as \"")
4699                                 .Append (info.EntryPoint)
4700                                 .Append ("\"");
4701                         if (info.IsCharSetAuto)
4702                                 buf.Append (" auto");
4703                         if (info.IsCharSetUnicode)
4704                                 buf.Append (" unicode");
4705                         if (info.IsCharSetAnsi)
4706                                 buf.Append (" ansi");
4707                         if (info.IsCallConvCdecl)
4708                                 buf.Append (" cdecl");
4709                         if (info.IsCallConvStdCall)
4710                                 buf.Append (" stdcall");
4711                         if (info.IsCallConvWinapi)
4712                                 buf.Append (" winapi");
4713                         if (info.IsCallConvThiscall)
4714                                 buf.Append (" thiscall");
4715                         if (info.SupportsLastError)
4716                                 buf.Append (" lasterr");
4717                         buf.Append (")");
4718                 }
4719                 if (method.IsSpecialName)
4720                         buf.Append ("specialname ");
4721                 if (method.IsRuntimeSpecialName)
4722                         buf.Append ("rtspecialname ");
4723                 if (method.IsNewSlot)
4724                         buf.Append ("newslot ");
4725                 if (method.IsVirtual)
4726                         buf.Append ("virtual ");
4727                 if (!method.IsStatic)
4728                         buf.Append ("instance ");
4729                 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4730                 buf.Append (' ')
4731                         .Append (method.Name);
4732                 if (method.IsGenericMethod ()) {
4733                         var state = MemberFormatterState;
4734                         MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4735                         IList<GenericParameter> args = method.GenericParameters;
4736                         if (args.Count > 0) {
4737                                 buf.Append ("<");
4738                                 _AppendTypeName (buf, args [0], null);
4739                                 for (int i = 1; i < args.Count; ++i)
4740                                         _AppendTypeName (buf.Append (", "), args [i], null);
4741                                 buf.Append (">");
4742                         }
4743                         MemberFormatterState = state;
4744                 }
4745
4746                 buf.Append ('(');
4747                 bool first = true;
4748                 for (int i = 0; i < method.Parameters.Count; ++i) {
4749                         if (!first)
4750                                 buf.Append (", ");
4751                         first = false;
4752                         _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4753                         buf.Append (' ');
4754                         buf.Append (method.Parameters [i].Name);
4755                 }
4756                 buf.Append (')');
4757                 if (method.IsIL)
4758                         buf.Append (" cil");
4759                 if (method.IsRuntime)
4760                         buf.Append (" runtime");
4761                 if (method.IsManaged)
4762                         buf.Append (" managed");
4763
4764                 return buf.ToString ();
4765         }
4766
4767         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4768         {
4769                 if (DocUtils.IsExplicitlyImplemented (method)) {
4770                         TypeReference iface;
4771                         MethodReference ifaceMethod;
4772                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4773                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4774                                 .Append ('.')
4775                                 .Append (ifaceMethod.Name);
4776                 }
4777                 return base.AppendMethodName (buf, method);
4778         }
4779
4780         protected override string RefTypeModifier {
4781                 get {return "";}
4782         }
4783
4784         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4785         {
4786                 if (method.IsPublic)
4787                         return buf.Append ("public ");
4788                 if (method.IsFamilyAndAssembly)
4789                         return buf.Append ("familyandassembly");
4790                 if (method.IsFamilyOrAssembly)
4791                         return buf.Append ("familyorassembly");
4792                 if (method.IsFamily)
4793                         return buf.Append ("family");
4794                 return buf;
4795         }
4796
4797         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4798         {
4799                 string modifiers = String.Empty;
4800                 if (method.IsStatic) modifiers += " static";
4801                 if (method.IsVirtual && !method.IsAbstract) {
4802                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4803                         else modifiers += " override";
4804                 }
4805                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4806                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4807                 if (method.IsFinal) modifiers += " sealed";
4808                 if (modifiers == " virtual sealed") modifiers = "";
4809
4810                 return buf.Append (modifiers);
4811         }
4812
4813         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4814         {
4815                 if (method.IsGenericMethod ()) {
4816                         IList<GenericParameter> args = method.GenericParameters;
4817                         if (args.Count > 0) {
4818                                 buf.Append ("<");
4819                                 buf.Append (args [0].Name);
4820                                 for (int i = 1; i < args.Count; ++i)
4821                                         buf.Append (",").Append (args [i].Name);
4822                                 buf.Append (">");
4823                         }
4824                 }
4825                 return buf;
4826         }
4827
4828         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4829         {
4830                 return AppendParameters (buf, method, parameters, '(', ')');
4831         }
4832
4833         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4834         {
4835                 buf.Append (begin);
4836
4837                 if (parameters.Count > 0) {
4838                         if (DocUtils.IsExtensionMethod (method))
4839                                 buf.Append ("this ");
4840                         AppendParameter (buf, parameters [0]);
4841                         for (int i = 1; i < parameters.Count; ++i) {
4842                                 buf.Append (", ");
4843                                 AppendParameter (buf, parameters [i]);
4844                         }
4845                 }
4846
4847                 return buf.Append (end);
4848         }
4849
4850         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4851         {
4852                 if (parameter.ParameterType is ByReferenceType) {
4853                         if (parameter.IsOut)
4854                                 buf.Append ("out ");
4855                         else
4856                                 buf.Append ("ref ");
4857                 }
4858                 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4859                 return buf.Append (parameter.Name);
4860         }
4861
4862         protected override string GetPropertyDeclaration (PropertyDefinition property)
4863         {
4864                 MethodDefinition gm = null, sm = null;
4865
4866                 string get_visible = null;
4867                 if ((gm = property.GetMethod) != null &&
4868                                 (DocUtils.IsExplicitlyImplemented (gm) ||
4869                                  (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4870                         get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4871                 string set_visible = null;
4872                 if ((sm = property.SetMethod) != null &&
4873                                 (DocUtils.IsExplicitlyImplemented (sm) ||
4874                                  (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4875                         set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4876
4877                 if ((set_visible == null) && (get_visible == null))
4878                         return null;
4879
4880                 StringBuilder buf = new StringBuilder ()
4881                         .Append (".property ");
4882                 if (!(gm ?? sm).IsStatic)
4883                         buf.Append ("instance ");
4884                 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4885                 buf.Append (' ').Append (property.Name);
4886                 if (!property.HasParameters || property.Parameters.Count == 0)
4887                         return buf.ToString ();
4888
4889                 buf.Append ('(');
4890                 bool first = true;
4891                 foreach (ParameterDefinition p in property.Parameters) {
4892                         if (!first)
4893                                 buf.Append (", ");
4894                         first = false;
4895                         _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4896                 }
4897                 buf.Append (')');
4898
4899                 return buf.ToString ();
4900         }
4901
4902         protected override string GetFieldDeclaration (FieldDefinition field)
4903         {
4904                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4905                 if (declType.IsEnum && field.Name == "value__")
4906                         return null; // This member of enums aren't documented.
4907
4908                 StringBuilder buf = new StringBuilder ();
4909                 AppendFieldVisibility (buf, field);
4910                 if (buf.Length == 0)
4911                         return null;
4912
4913                 buf.Insert (0, ".field ");
4914
4915                 if (field.IsStatic)
4916                         buf.Append ("static ");
4917                 if (field.IsInitOnly)
4918                         buf.Append ("initonly ");
4919                 if (field.IsLiteral)
4920                         buf.Append ("literal ");
4921                 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4922                 buf.Append (' ').Append (field.Name);
4923                 AppendFieldValue (buf, field);
4924
4925                 return buf.ToString ();
4926         }
4927
4928         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4929         {
4930                 if (field.IsPublic)
4931                         return buf.Append ("public ");
4932                 if (field.IsFamilyAndAssembly)
4933                         return buf.Append ("familyandassembly ");
4934                 if (field.IsFamilyOrAssembly)
4935                         return buf.Append ("familyorassembly ");
4936                 if (field.IsFamily)
4937                         return buf.Append ("family ");
4938                 return buf;
4939         }
4940
4941         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4942         {
4943                 // enums have a value__ field, which we ignore
4944                 if (field.DeclaringType.IsGenericType ())
4945                         return buf;
4946                 if (field.HasConstant && field.IsLiteral) {
4947                         object val = null;
4948                         try {
4949                                 val   = field.Constant;
4950                         } catch {
4951                                 return buf;
4952                         }
4953                         if (val == null)
4954                                 buf.Append (" = ").Append ("null");
4955                         else if (val is Enum)
4956                                 buf.Append (" = ")
4957                                         .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4958                                         .Append ('(')
4959                                         .Append (val.ToString ())
4960                                         .Append (')');
4961                         else if (val is IFormattable) {
4962                                 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
4963                                 buf.Append (" = ");
4964                                 if (val is string)
4965                                         buf.Append ("\"" + value + "\"");
4966                                 else
4967                                         buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4968                                                 .Append ('(')
4969                                                 .Append (value)
4970                                                 .Append (')');
4971                         }
4972                 }
4973                 return buf;
4974         }
4975
4976         protected override string GetEventDeclaration (EventDefinition e)
4977         {
4978                 StringBuilder buf = new StringBuilder ();
4979                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4980                         return null;
4981                 }
4982
4983                 buf.Length = 0;
4984                 buf.Append (".event ")
4985                         .Append (GetName (e.EventType))
4986                         .Append (' ')
4987                         .Append (e.Name);
4988
4989                 return buf.ToString ();
4990         }
4991 }
4992
4993 class ILMemberFormatter : ILFullMemberFormatter {
4994         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4995         {
4996                 return buf;
4997         }
4998 }
4999
5000         class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
5001                 protected static string _GetBuiltinType (string t)
5002                 {
5003                         //string moddedType = base.GetBuiltinType (t);
5004                         return null;
5005                         //return moddedType;
5006                 }
5007         }
5008
5009         class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
5010                 protected override string GetCSharpType (string t) {
5011                         string moddedType = base.GetCSharpType (t);
5012
5013                         switch (moddedType) {
5014                         case "int":             return "nint";
5015                         case "uint":
5016                                 return "nuint";
5017                         case "float":
5018                                 return "nfloat";
5019                         case "System.Drawing.SizeF":
5020                                 return "CoreGraphics.CGSize";
5021                         case "System.Drawing.PointF":
5022                                 return "CoreGraphics.CGPoint";
5023                         case "System.Drawing.RectangleF":
5024                                 return "CoreGraphics.CGPoint";
5025                         }
5026                         return null;
5027                 }
5028         }
5029
5030 class CSharpFullMemberFormatter : MemberFormatter {
5031
5032         public override string Language {
5033                 get {return "C#";}
5034         }
5035
5036         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5037         {
5038
5039                 string ns = DocUtils.GetNamespace (type);
5040                 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
5041                         buf.Append (ns).Append ('.');
5042                 return buf;
5043         }
5044
5045         protected virtual string GetCSharpType (string t)
5046         {
5047                 switch (t) {
5048                 case "System.Byte":    return "byte";
5049                 case "System.SByte":   return "sbyte";
5050                 case "System.Int16":   return "short";
5051                 case "System.Int32":   return "int";
5052                 case "System.Int64":   return "long";
5053
5054                 case "System.UInt16":  return "ushort";
5055                 case "System.UInt32":  return "uint";
5056                 case "System.UInt64":  return "ulong";
5057
5058                 case "System.Single":  return "float";
5059                 case "System.Double":  return "double";
5060                 case "System.Decimal": return "decimal";
5061                 case "System.Boolean": return "bool";
5062                 case "System.Char":    return "char";
5063                 case "System.Void":    return "void";
5064                 case "System.String":  return "string";
5065                 case "System.Object":  return "object";
5066                 }
5067                 return null;
5068         }
5069
5070         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5071         {
5072                 if (context != null && context.TransformFlags != null &&
5073                                 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5074                         context.TransformIndex++;
5075                         return buf.Append ("dynamic");
5076                 }
5077
5078                 if (type is GenericParameter)
5079                         return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5080                 string t = type.FullName;
5081                 if (!t.StartsWith ("System.")) {
5082                         return base.AppendTypeName (buf, type, context);
5083                 }
5084
5085                 string s = GetCSharpType (t);
5086                 if (s != null) {
5087                         if (context != null)
5088                                 context.TransformIndex++;
5089                         return buf.Append (s);
5090                 }
5091                 
5092                 return base.AppendTypeName (buf, type, context);
5093         }
5094
5095         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5096         {
5097                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5098                         return buf;
5099                 GenericParameterAttributes attrs = type.Attributes;
5100                 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5101                 bool isin  = (attrs & GenericParameterAttributes.Contravariant) != 0;
5102                 if (isin)
5103                         buf.Append ("in ");
5104                 else if (isout)
5105                         buf.Append ("out ");
5106                 return buf;
5107         }
5108
5109         protected override string GetTypeDeclaration (TypeDefinition type)
5110         {
5111                 string visibility = GetTypeVisibility (type.Attributes);
5112                 if (visibility == null)
5113                         return null;
5114
5115                 StringBuilder buf = new StringBuilder ();
5116                 
5117                 buf.Append (visibility);
5118                 buf.Append (" ");
5119
5120                 MemberFormatter full = new CSharpFullMemberFormatter ();
5121
5122                 if (DocUtils.IsDelegate (type)) {
5123                         buf.Append("delegate ");
5124                         MethodDefinition invoke = type.GetMethod ("Invoke");
5125                         buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5126                         buf.Append (GetName (type));
5127                         AppendParameters (buf, invoke, invoke.Parameters);
5128                         AppendGenericTypeConstraints (buf, type);
5129                         buf.Append (";");
5130
5131                         return buf.ToString();
5132                 }
5133                 
5134                 if (type.IsAbstract && !type.IsInterface)
5135                         buf.Append("abstract ");
5136                 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5137                         buf.Append("sealed ");
5138                 buf.Replace ("abstract sealed", "static");
5139
5140                 buf.Append (GetTypeKind (type));
5141                 buf.Append (" ");
5142                 buf.Append (GetCSharpType (type.FullName) == null 
5143                                 ? GetName (type) 
5144                                 : type.Name);
5145
5146                 if (!type.IsEnum) {
5147                         TypeReference basetype = type.BaseType;
5148                         if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType)       // FIXME
5149                                 basetype = null;
5150
5151                         List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5152                                         .Select (iface => full.GetName (iface))
5153                                         .OrderBy (s => s)
5154                                         .ToList ();
5155
5156                         if (basetype != null || interface_names.Count > 0)
5157                                 buf.Append (" : ");
5158                         
5159                         if (basetype != null) {
5160                                 buf.Append (full.GetName (basetype));
5161                                 if (interface_names.Count > 0)
5162                                         buf.Append (", ");
5163                         }
5164                         
5165                         for (int i = 0; i < interface_names.Count; i++){
5166                                 if (i != 0)
5167                                         buf.Append (", ");
5168                                 buf.Append (interface_names [i]);
5169                         }
5170                         AppendGenericTypeConstraints (buf, type);
5171                 }
5172
5173                 return buf.ToString ();
5174         }
5175
5176         static string GetTypeKind (TypeDefinition t)
5177         {
5178                 if (t.IsEnum)
5179                         return "enum";
5180                 if (t.IsValueType)
5181                         return "struct";
5182                 if (t.IsClass || t.FullName == "System.Enum")
5183                         return "class";
5184                 if (t.IsInterface)
5185                         return "interface";
5186                 throw new ArgumentException(t.FullName);
5187         }
5188
5189         static string GetTypeVisibility (TypeAttributes ta)
5190         {
5191                 switch (ta & TypeAttributes.VisibilityMask) {
5192                 case TypeAttributes.Public:
5193                 case TypeAttributes.NestedPublic:
5194                         return "public";
5195
5196                 case TypeAttributes.NestedFamily:
5197                 case TypeAttributes.NestedFamORAssem:
5198                         return "protected";
5199
5200                 default:
5201                         return null;
5202                 }
5203         }
5204
5205         protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5206         {
5207                 if (type.GenericParameters.Count == 0)
5208                         return buf;
5209                 return AppendConstraints (buf, type.GenericParameters);
5210         }
5211
5212         private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5213         {
5214                 foreach (GenericParameter genArg in genArgs) {
5215                         GenericParameterAttributes attrs = genArg.Attributes;
5216                         IList<TypeReference> constraints = genArg.Constraints;
5217                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5218                                 continue;
5219
5220                         bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5221                         bool isvt  = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5222                         bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5223                         bool comma = false;
5224
5225                         if (!isref && !isvt && !isnew && constraints.Count == 0)
5226                                 continue;
5227                         buf.Append (" where ").Append (genArg.Name).Append (" : ");
5228                         if (isref) {
5229                                 buf.Append ("class");
5230                                 comma = true;
5231                         }
5232                         else if (isvt) {
5233                                 buf.Append ("struct");
5234                                 comma = true;
5235                         }
5236                         if (constraints.Count > 0 && !isvt) {
5237                                 if (comma)
5238                                         buf.Append (", ");
5239                                 buf.Append (GetTypeName (constraints [0]));
5240                                 for (int i = 1; i < constraints.Count; ++i)
5241                                         buf.Append (", ").Append (GetTypeName (constraints [i]));
5242                         }
5243                         if (isnew && !isvt) {
5244                                 if (comma)
5245                                         buf.Append (", ");
5246                                 buf.Append ("new()");
5247                         }
5248                 }
5249                 return buf;
5250         }
5251
5252         protected override string GetConstructorDeclaration (MethodDefinition constructor)
5253         {
5254                 StringBuilder buf = new StringBuilder ();
5255                 AppendVisibility (buf, constructor);
5256                 if (buf.Length == 0)
5257                         return null;
5258
5259                 buf.Append (' ');
5260                 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5261                 AppendParameters (buf, constructor, constructor.Parameters);
5262                 buf.Append (';');
5263
5264                 return buf.ToString ();
5265         }
5266         
5267         protected override string GetMethodDeclaration (MethodDefinition method)
5268         {
5269                 string decl = base.GetMethodDeclaration (method);
5270                 if (decl != null)
5271                         return decl + ";";
5272                 return null;
5273         }
5274
5275         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5276         {
5277                 if (DocUtils.IsExplicitlyImplemented (method)) {
5278                         TypeReference iface;
5279                         MethodReference ifaceMethod;
5280                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5281                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5282                                 .Append ('.')
5283                                 .Append (ifaceMethod.Name);
5284                 }
5285                 return base.AppendMethodName (buf, method);
5286         }
5287
5288         protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5289         {
5290                 if (method.GenericParameters.Count == 0)
5291                         return buf;
5292                 return AppendConstraints (buf, method.GenericParameters);
5293         }
5294
5295         protected override string RefTypeModifier {
5296                 get {return "";}
5297         }
5298
5299         protected override string GetFinalizerName (MethodDefinition method)
5300         {
5301                 return "~" + method.DeclaringType.Name + " ()"; 
5302         }
5303
5304         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5305         {
5306                 if (method == null)
5307                         return buf;
5308                 if (method.IsPublic)
5309                         return buf.Append ("public");
5310                 if (method.IsFamily || method.IsFamilyOrAssembly)
5311                         return buf.Append ("protected");
5312                 return buf;
5313         }
5314
5315         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5316         {
5317                 string modifiers = String.Empty;
5318                 if (method.IsStatic) modifiers += " static";
5319                 if (method.IsVirtual && !method.IsAbstract) {
5320                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5321                         else modifiers += " override";
5322                 }
5323                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5324                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5325                 if (method.IsFinal) modifiers += " sealed";
5326                 if (modifiers == " virtual sealed") modifiers = "";
5327
5328                 return buf.Append (modifiers);
5329         }
5330
5331         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5332         {
5333                 if (method.IsGenericMethod ()) {
5334                         IList<GenericParameter> args = method.GenericParameters;
5335                         if (args.Count > 0) {
5336                                 buf.Append ("<");
5337                                 buf.Append (args [0].Name);
5338                                 for (int i = 1; i < args.Count; ++i)
5339                                         buf.Append (",").Append (args [i].Name);
5340                                 buf.Append (">");
5341                         }
5342                 }
5343                 return buf;
5344         }
5345
5346         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5347         {
5348                 return AppendParameters (buf, method, parameters, '(', ')');
5349         }
5350
5351         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5352         {
5353                 buf.Append (begin);
5354
5355                 if (parameters.Count > 0) {
5356                         if (DocUtils.IsExtensionMethod (method))
5357                                 buf.Append ("this ");
5358                         AppendParameter (buf, parameters [0]);
5359                         for (int i = 1; i < parameters.Count; ++i) {
5360                                 buf.Append (", ");
5361                                 AppendParameter (buf, parameters [i]);
5362                         }
5363                 }
5364
5365                 return buf.Append (end);
5366         }
5367
5368         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5369         {
5370                 if (parameter.ParameterType is ByReferenceType) {
5371                         if (parameter.IsOut)
5372                                 buf.Append ("out ");
5373                         else
5374                                 buf.Append ("ref ");
5375                 }
5376                 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5377                 buf.Append (parameter.Name);
5378                 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5379                         buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5380                 }
5381                 return buf;
5382         }
5383
5384         protected override string GetPropertyDeclaration (PropertyDefinition property)
5385         {
5386                 MethodDefinition method;
5387
5388                 string get_visible = null;
5389                 if ((method = property.GetMethod) != null && 
5390                                 (DocUtils.IsExplicitlyImplemented (method) || 
5391                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5392                         get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5393                 string set_visible = null;
5394                 if ((method = property.SetMethod) != null &&
5395                                 (DocUtils.IsExplicitlyImplemented (method) || 
5396                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5397                         set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5398
5399                 if ((set_visible == null) && (get_visible == null))
5400                         return null;
5401
5402                 string visibility;
5403                 StringBuilder buf = new StringBuilder ();
5404                 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5405                         buf.Append (visibility = get_visible);
5406                 else if (set_visible != null && get_visible == null)
5407                         buf.Append (visibility = set_visible);
5408                 else
5409                         buf.Append (visibility = "public");
5410
5411                 // Pick an accessor to use for static/virtual/override/etc. checks.
5412                 method = property.SetMethod;
5413                 if (method == null)
5414                         method = property.GetMethod;
5415         
5416                 string modifiers = String.Empty;
5417                 if (method.IsStatic) modifiers += " static";
5418                 if (method.IsVirtual && !method.IsAbstract) {
5419                                 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5420                                         modifiers += " virtual";
5421                                 else
5422                                         modifiers += " override";
5423                 }
5424                 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5425                 if (method.IsAbstract && !declDef.IsInterface)
5426                         modifiers += " abstract";
5427                 if (method.IsFinal)
5428                         modifiers += " sealed";
5429                 if (modifiers == " virtual sealed")
5430                         modifiers = "";
5431                 buf.Append (modifiers).Append (' ');
5432
5433                 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5434
5435                 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5436                 string name = property.Name;
5437                 foreach (MemberReference mi in defs) {
5438                         if (mi == property) {
5439                                 name = "this";
5440                                 break;
5441                         }
5442                 }
5443                 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5444         
5445                 if (property.Parameters.Count != 0) {
5446                         AppendParameters (buf, method, property.Parameters, '[', ']');
5447                 }
5448
5449                 buf.Append (" {");
5450                 if (get_visible != null) {
5451                         if (get_visible != visibility)
5452                                 buf.Append (' ').Append (get_visible);
5453                         buf.Append (" get;");
5454                 }
5455                 if (set_visible != null) {
5456                         if (set_visible != visibility)
5457                                 buf.Append (' ').Append (set_visible);
5458                         buf.Append (" set;");
5459                 }
5460                 buf.Append (" }");
5461         
5462                 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5463         }
5464
5465         protected override string GetFieldDeclaration (FieldDefinition field)
5466         {
5467                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5468                 if (declType.IsEnum && field.Name == "value__")
5469                         return null; // This member of enums aren't documented.
5470
5471                 StringBuilder buf = new StringBuilder ();
5472                 AppendFieldVisibility (buf, field);
5473                 if (buf.Length == 0)
5474                         return null;
5475
5476                 if (declType.IsEnum)
5477                         return field.Name;
5478
5479                 if (field.IsStatic && !field.IsLiteral)
5480                         buf.Append (" static");
5481                 if (field.IsInitOnly)
5482                         buf.Append (" readonly");
5483                 if (field.IsLiteral)
5484                         buf.Append (" const");
5485
5486                 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5487                 buf.Append (field.Name);
5488                 AppendFieldValue (buf, field);
5489                 buf.Append (';');
5490
5491                 return buf.ToString ();
5492         }
5493
5494         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5495         {
5496                 if (field.IsPublic)
5497                         return buf.Append ("public");
5498                 if (field.IsFamily || field.IsFamilyOrAssembly)
5499                         return buf.Append ("protected");
5500                 return buf;
5501         }
5502
5503         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5504         {
5505                 // enums have a value__ field, which we ignore
5506                 if (((TypeDefinition ) field.DeclaringType).IsEnum || 
5507                                 field.DeclaringType.IsGenericType ())
5508                         return buf;
5509                 if (field.HasConstant && field.IsLiteral) {
5510                         object val = null;
5511                         try {
5512                                 val   = field.Constant;
5513                         } catch {
5514                                 return buf;
5515                         }
5516                         if (val == null)
5517                                 buf.Append (" = ").Append ("null");
5518                         else if (val is Enum)
5519                                 buf.Append (" = ").Append (val.ToString ());
5520                         else if (val is IFormattable) {
5521                                 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
5522                                 if (val is string)
5523                                         value = "\"" + value + "\"";
5524                                 buf.Append (" = ").Append (value);
5525                         }
5526                 }
5527                 return buf;
5528         }
5529
5530         protected override string GetEventDeclaration (EventDefinition e)
5531         {
5532                 StringBuilder buf = new StringBuilder ();
5533                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5534                         return null;
5535                 }
5536
5537                 AppendModifiers (buf, e.AddMethod);
5538
5539                 buf.Append (" event ");
5540                 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5541                 buf.Append (e.Name).Append (';');
5542
5543                 return buf.ToString ();
5544         }
5545 }
5546
5547 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5548         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5549         {
5550                 return buf;
5551         }
5552 }
5553
5554 class DocTypeFullMemberFormatter : MemberFormatter {
5555         public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5556
5557         protected override char NestedTypeSeparator {
5558                 get {return '+';}
5559         }
5560 }
5561
5562 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5563         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5564         {
5565                 return buf;
5566         }
5567 }
5568
5569 class SlashDocMemberFormatter : MemberFormatter {
5570
5571         protected override char[] GenericTypeContainer {
5572                 get {return new char[]{'{', '}'};}
5573         }
5574
5575         private bool AddTypeCount = true;
5576
5577         private TypeReference genDeclType;
5578         private MethodReference genDeclMethod;
5579
5580         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5581         {
5582                 if (type is GenericParameter) {
5583                         int l = buf.Length;
5584                         if (genDeclType != null) {
5585                                 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5586                                 for (int i = 0; i < genArgs.Count; ++i) {
5587                                         if (genArgs [i].Name == type.Name) {
5588                                                 buf.Append ('`').Append (i);
5589                                                 break;
5590                                         }
5591                                 }
5592                         }
5593                         if (genDeclMethod != null) {
5594                                 IList<GenericParameter> genArgs = null;
5595                                 if (genDeclMethod.IsGenericMethod ()) {
5596                                         genArgs = genDeclMethod.GenericParameters;
5597                                         for (int i = 0; i < genArgs.Count; ++i) {
5598                                                 if (genArgs [i].Name == type.Name) {
5599                                                         buf.Append ("``").Append (i);
5600                                                         break;
5601                                                 }
5602                                         }
5603                                 }
5604                         }
5605                         if (genDeclType == null && genDeclMethod == null) {
5606                                 // Probably from within an explicitly implemented interface member,
5607                                 // where CSC uses parameter names instead of indices (why?), e.g.
5608                                 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5609                                 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5610                                 buf.Append (type.Name);
5611                         }
5612                         if (buf.Length == l) {
5613                                 throw new Exception (string.Format (
5614                                                 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}", 
5615                                                 type.Name, genDeclType, genDeclMethod));
5616                         }
5617                 }
5618                 else {
5619                         base.AppendTypeName (buf, type, context);
5620                         if (AddTypeCount) {
5621                                 int numArgs = type.GenericParameters.Count;
5622                                 if (type.DeclaringType != null)
5623                                         numArgs -= type.GenericParameters.Count;
5624                                 if (numArgs > 0) {
5625                                         buf.Append ('`').Append (numArgs);
5626                                 }
5627                         }
5628                 }
5629                 return buf;
5630         }
5631
5632         protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5633         {
5634                 buf.Append (ArrayDelimeters [0]);
5635                 int rank = array.Rank;
5636                 if (rank > 1) {
5637                         buf.Append ("0:");
5638                         for (int i = 1; i < rank; ++i) {
5639                                 buf.Append (",0:");
5640                         }
5641                 }
5642                 return buf.Append (ArrayDelimeters [1]);
5643         }
5644
5645         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5646         {
5647                 if (!AddTypeCount)
5648                         base.AppendGenericType (buf, type, context);
5649                 else
5650                         AppendType (buf, type, context);
5651                 return buf;
5652         }
5653
5654         private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5655         {
5656                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5657                 bool insertNested = false;
5658                 int prevParamCount = 0;
5659                 foreach (var decl in decls) {
5660                         if (insertNested)
5661                                 buf.Append (NestedTypeSeparator);
5662                         insertNested = true;
5663                         base.AppendTypeName (buf, decl, context);
5664                         int argCount = DocUtils.GetGenericArgumentCount (decl);
5665                         int numArgs = argCount - prevParamCount;
5666                         prevParamCount = argCount;
5667                         if (numArgs > 0)
5668                                 buf.Append ('`').Append (numArgs);
5669                 }
5670                 return buf;
5671         }
5672
5673         protected override string GetConstructorName (MethodReference constructor)
5674         {
5675                 return GetMethodDefinitionName (constructor, "#ctor");
5676         }
5677
5678         protected override string GetMethodName (MethodReference method)
5679         {
5680                 string name = null;
5681                 MethodDefinition methodDef = method as MethodDefinition;
5682                 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5683                         name = method.Name;
5684                 else {
5685                         TypeReference iface;
5686                         MethodReference ifaceMethod;
5687                         DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5688                         AddTypeCount = false;
5689                         name = GetTypeName (iface) + "." + ifaceMethod.Name;
5690                         AddTypeCount = true;
5691                 }
5692                 return GetMethodDefinitionName (method, name);
5693         }
5694
5695         private string GetMethodDefinitionName (MethodReference method, string name)
5696         {
5697                 StringBuilder buf = new StringBuilder ();
5698                 buf.Append (GetTypeName (method.DeclaringType));
5699                 buf.Append ('.');
5700                 buf.Append (name.Replace (".", "#"));
5701                 if (method.IsGenericMethod ()) {
5702                         IList<GenericParameter> genArgs = method.GenericParameters;
5703                         if (genArgs.Count > 0)
5704                                 buf.Append ("``").Append (genArgs.Count);
5705                 }
5706                 IList<ParameterDefinition> parameters = method.Parameters;
5707                 try {
5708                         genDeclType   = method.DeclaringType;
5709                         genDeclMethod = method;
5710                         AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5711                 }
5712                 finally {
5713                         genDeclType   = null;
5714                         genDeclMethod = null;
5715                 }
5716                 return buf.ToString ();
5717         }
5718
5719         private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5720         {
5721                 if (parameters.Count == 0)
5722                         return buf;
5723
5724                 buf.Append ('(');
5725
5726                 AppendParameter (buf, genArgs, parameters [0]);
5727                 for (int i = 1; i < parameters.Count; ++i) {
5728                         buf.Append (',');
5729                         AppendParameter (buf, genArgs, parameters [i]);
5730                 }
5731
5732                 return buf.Append (')');
5733         }
5734
5735         private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5736         {
5737                 AddTypeCount = false;
5738                 buf.Append (GetTypeName (parameter.ParameterType));
5739                 AddTypeCount = true;
5740                 return buf;
5741         }
5742
5743         protected override string GetPropertyName (PropertyReference property)
5744         {
5745                 string name = null;
5746
5747                 PropertyDefinition propertyDef = property as PropertyDefinition;
5748                 MethodDefinition method = null;
5749                 if (propertyDef != null)
5750                         method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5751                 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5752                         name = property.Name;
5753                 else {
5754                         TypeReference iface;
5755                         MethodReference ifaceMethod;
5756                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5757                         AddTypeCount = false;
5758                         name = string.Join ("#", new string[]{
5759                                         GetTypeName (iface).Replace (".", "#"),
5760                                         DocUtils.GetMember (property.Name)
5761                         });
5762                         AddTypeCount = true;
5763                 }
5764
5765                 StringBuilder buf = new StringBuilder ();
5766                 buf.Append (GetName (property.DeclaringType));
5767                 buf.Append ('.');
5768                 buf.Append (name);
5769                 IList<ParameterDefinition> parameters = property.Parameters;
5770                 if (parameters.Count > 0) {
5771                         genDeclType = property.DeclaringType;
5772                         buf.Append ('(');
5773                         IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5774                         AppendParameter (buf, genArgs, parameters [0]);
5775                         for (int i = 1; i < parameters.Count; ++i) {
5776                                  buf.Append (',');
5777                                  AppendParameter (buf, genArgs, parameters [i]);
5778                         }
5779                         buf.Append (')');
5780                         genDeclType = null;
5781                 }
5782                 return buf.ToString ();
5783         }
5784
5785         protected override string GetFieldName (FieldReference field)
5786         {
5787                 return string.Format ("{0}.{1}",
5788                         GetName (field.DeclaringType), field.Name);
5789         }
5790
5791         protected override string GetEventName (EventReference e)
5792         {
5793                 return string.Format ("{0}.{1}",
5794                         GetName (e.DeclaringType), e.Name);
5795         }
5796
5797         protected override string GetTypeDeclaration (TypeDefinition type)
5798         {
5799                 string name = GetName (type);
5800                 if (type == null)
5801                         return null;
5802                 return "T:" + name;
5803         }
5804
5805         protected override string GetConstructorDeclaration (MethodDefinition constructor)
5806         {
5807                 string name = GetName (constructor);
5808                 if (name == null)
5809                         return null;
5810                 return "M:" + name;
5811         }
5812
5813         protected override string GetMethodDeclaration (MethodDefinition method)
5814         {
5815                 string name = GetName (method);
5816                 if (name == null)
5817                         return null;
5818                 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5819                         genDeclType = method.DeclaringType;
5820                         genDeclMethod = method;
5821                         name += "~" + GetName (method.ReturnType);
5822                         genDeclType = null;
5823                         genDeclMethod = null;
5824                 }
5825                 return "M:" + name;
5826         }
5827
5828         protected override string GetPropertyDeclaration (PropertyDefinition property)
5829         {
5830                 string name = GetName (property);
5831                 if (name == null)
5832                         return null;
5833                 return "P:" + name;
5834         }
5835
5836         protected override string GetFieldDeclaration (FieldDefinition field)
5837         {
5838                 string name = GetName (field);
5839                 if (name == null)
5840                         return null;
5841                 return "F:" + name;
5842         }
5843
5844         protected override string GetEventDeclaration (EventDefinition e)
5845         {
5846                 string name = GetName (e);
5847                 if (name == null)
5848                         return null;
5849                 return "E:" + name;
5850         }
5851 }
5852
5853 class FileNameMemberFormatter : SlashDocMemberFormatter {
5854         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5855         {
5856                 return buf;
5857         }
5858
5859         protected override char NestedTypeSeparator {
5860                 get {return '+';}
5861         }
5862 }
5863
5864 class ResolvedTypeInfo {
5865         TypeDefinition typeDef;
5866
5867         public ResolvedTypeInfo (TypeReference value) {
5868                 Reference = value;
5869         }
5870
5871         public TypeReference Reference { get; private set; }
5872
5873         public TypeDefinition Definition {
5874                 get {
5875                         if (typeDef == null) {
5876                                 typeDef = Reference.Resolve ();
5877                         }
5878                         return typeDef;
5879                 }
5880         }
5881 }
5882
5883 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5884 class AttributeValueFormatter {
5885         public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5886         {
5887                 TypeReference valueType = type.Reference;
5888                 if (v == null) {
5889                         returnvalue = "null";
5890                         return true;
5891                 }
5892                 if (valueType.FullName == "System.Type") {
5893                         var vTypeRef = v as TypeReference;
5894                         if (vTypeRef != null) 
5895                                 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5896                         else
5897                                 returnvalue = "typeof(" + v.ToString () + ")";
5898                         
5899                         return true;
5900                 }
5901                 if (valueType.FullName == "System.String") {
5902                         returnvalue = "\"" + v.ToString () + "\"";
5903                         return true;
5904                 }
5905                 if (valueType.FullName == "System.Char") {
5906                         returnvalue = "'" + v.ToString () + "'";
5907                         return true;
5908                 }
5909                 if (v is Boolean) {
5910                         returnvalue = (bool)v ? "true" : "false";
5911                         return true;
5912                 }
5913
5914                 TypeDefinition valueDef = type.Definition;
5915                 if (valueDef == null || !valueDef.IsEnum) {
5916                         returnvalue = v.ToString ();
5917                         return true;
5918                 }
5919
5920                 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5921                 var values = MDocUpdater.GetEnumerationValues (valueDef);
5922                 long c = MDocUpdater.ToInt64 (v);
5923                 if (values.ContainsKey (c)) {
5924                         returnvalue = typename + "." + values [c];
5925                         return true;
5926                 }
5927
5928                 returnvalue = null;
5929                 return false;
5930         }
5931 }
5932
5933 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5934 /// then this one will serve as the default implementation.</summary>
5935 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5936         public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5937         {
5938                 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5939                 return true;
5940         }
5941 }
5942
5943 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5944 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5945 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5946         public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5947         {
5948                 TypeReference valueType = type.Reference;
5949                 TypeDefinition valueDef = type.Definition;
5950                 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5951
5952                         string typename = MDocUpdater.GetDocTypeFullName (valueType);
5953                         var values = MDocUpdater.GetEnumerationValues (valueDef);
5954                         long c = MDocUpdater.ToInt64 (v);
5955                         returnvalue = string.Join (" | ",
5956                                 (from i in values.Keys
5957                                  where (c & i) == i && i != 0
5958                                  select typename + "." + values [i])
5959                                 .DefaultIfEmpty (c.ToString ()).ToArray ());
5960                         
5961                         return true;
5962                 }
5963
5964                 returnvalue = null;
5965                 return false;
5966         }
5967 }
5968
5969 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5970 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5971         public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5972         {
5973                 TypeReference valueType = type.Reference;
5974                 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5975                 TypeDefinition valueDef = type.Definition;
5976                 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5977
5978                         var values = MDocUpdater.GetEnumerationValues (valueDef);
5979                         long c = MDocUpdater.ToInt64 (v);
5980
5981                         returnvalue = Format (c, values, typename);
5982                         return true;
5983                 }
5984
5985                 returnvalue = null;
5986                 return false;
5987         }
5988
5989         string Format (long c, IDictionary<long, string> values, string typename)
5990         {
5991                 int iosarch, iosmajor, iosminor, iossubminor;
5992                 int macarch, macmajor, macminor, macsubminor;
5993                 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5994                 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5995
5996                 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5997                         return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5998                 }
5999
6000                 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
6001                         return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
6002                 }
6003
6004                 return string.Format ("(Platform){0}", c);
6005         }
6006
6007         string FormatValues (string plat, int arch, int major, int minor, int subminor) 
6008         {
6009                 string archstring = "";
6010                 switch (arch) {
6011                 case 1:
6012                         archstring = "32";
6013                         break;
6014                 case 2:
6015                         archstring = "64";
6016                         break;
6017                 }
6018                 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6019                         major,
6020                         minor,
6021                         subminor == 0 ? "" : "_" + subminor.ToString (),
6022                         archstring,
6023                         plat
6024                 );
6025         }
6026
6027         void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
6028         {
6029                 long lowerBits = entireLong & 0xffffffff; 
6030                 int lowerBitsAsInt = (int) lowerBits;
6031                 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
6032         }
6033
6034         void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
6035         {
6036                 ulong higherBits = entireLong & 0xffffffff00000000; 
6037                 int higherBitsAsInt = (int) ((higherBits) >> 32);
6038                 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
6039         }
6040
6041         void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
6042         {
6043                 // format is AAJJNNSS
6044                 archindex = (int)((encodedBits & 0xFF000000) >> 24);
6045                 major = (int)((encodedBits & 0x00FF0000) >> 16);
6046                 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6047                 subminor = (int)((encodedBits & 0x000000FF) >> 0);
6048         }
6049 }
6050 }