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