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