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