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