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