* Mono.Documentation/mdoc.cs: `mdoc' (no arguments) shouldn't print an
[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.Globalization;
9 using System.IO;
10 using System.Linq;
11 using System.Text;
12 using System.Xml;
13 using System.Xml.XPath;
14
15 using Mono.Cecil;
16 using Mono.Options;
17
18 using MyXmlNodeList        = System.Collections.Generic.List<System.Xml.XmlNode>;
19 using StringList           = System.Collections.Generic.List<string>;
20 using StringToStringMap    = System.Collections.Generic.Dictionary<string, string>;
21 using StringToXmlNodeMap   = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
22
23 namespace Mono.Documentation {
24
25 class MDocUpdaterOptions 
26 {
27         public string path = null;
28         public string updateto = null;
29         public List<string> assembly = null;
30         public List<string> type = null;
31         public string @namespace = null;
32         public bool delete = false;
33         public bool overrides = true;
34         public bool ignoremembers = false;
35         public bool ignore_extra_docs = false;
36         public bool no_assembly_versions = false;
37         public string name;
38         public string import;
39         public bool pretty = true;
40         public string since;
41         public bool show_exceptions;
42         public ExceptionLocations? exceptions;
43 }
44
45 class MDocUpdater : MDocCommand
46 {
47         
48         static string srcPath;
49         static List<AssemblyDefinition> assemblies;
50         
51         static bool nooverrides = true, delete = false, ignoremembers = false;
52         static bool pretty = false;
53         static bool show_exceptions = false;
54         static bool no_assembly_versions;
55         static ExceptionLocations? exceptions;
56         
57         static int additions = 0, deletions = 0;
58
59         static string name;
60         static XmlDocument slashdocs;
61         static XmlReader ecmadocs;
62
63         static string since;
64
65         static MemberFormatter csharpFullFormatter  = new CSharpFullMemberFormatter ();
66         static MemberFormatter csharpFormatter      = new CSharpMemberFormatter ();
67         static MemberFormatter docTypeFormatter     = new DocTypeMemberFormatter ();
68         static MemberFormatter slashdocFormatter    = new SlashDocMemberFormatter ();
69         static MemberFormatter filenameFormatter    = new FileNameMemberFormatter ();
70
71         static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
72
73         public override void Run (IEnumerable<string> args)
74         {
75                 var opts = new MDocUpdaterOptions {
76                         overrides        = true,
77                         pretty           = true,
78                         show_exceptions  = DebugOutput,
79                 };
80
81                 opts.type = new List<string> ();
82                 var p = new OptionSet () {
83                         { "delete",
84                                 "Delete removed members from the XML files.",
85                                 v => opts.delete = v != null },
86                         { "exceptions:",
87                           "Document potential exceptions that members can generate.  {SOURCES} " +
88                                 "is a comma-separated list of:\n" +
89                                 "  asm      Method calls in same assembly\n" +
90                                 "  depasm   Method calls in dependent assemblies\n" +
91                                 "  all      Record all possible exceptions\n" +
92                                 "If nothing is specified, then only exceptions from the member will " +
93                                 "be listed.",
94                                 v => opts.exceptions = ParseExceptionLocations (v) },
95                         { "f=",
96                                 "Specify a {FLAG} to alter behavior.  See later -f* options for available flags.",
97                                 v => {
98                                         switch (v) {
99                                                 case "no-assembly-versions":
100                                                         opts.no_assembly_versions = true;
101                                                         break;
102                                                 default:
103                                                         throw new Exception ("Unsupported flag `" + v + "'.");
104                                         }
105                                 } },
106                         { "fno-assembly-versions",
107                                 "Do not generate //AssemblyVersion elements.",
108                                 v => opts.no_assembly_versions = v != null },
109                         { "i|import=", 
110                                 "Import documentation from {FILE}.",
111                                 v => opts.import = v },
112                         { "o|out=",
113                                 "Root {DIRECTORY} to generate/update documentation.",
114                                 v => opts.path = v },
115                         { "since=",
116                                 "Manually specify the assembly {VERSION} that new members were added in.",
117                                 v => opts.since = v },
118                         { "type=",
119                           "Only update documentation for {TYPE}.",
120                                 v => opts.type.Add (v) },
121                 };
122                 opts.assembly = Parse (p, args, "update", 
123                                 "[OPTIONS]+ ASSEMBLIES",
124                                 "Create or update documentation from ASSEMBLIES.");
125                 if (opts.assembly == null)
126                         return;
127                 if (opts.assembly.Count == 0)
128                         base.Error ("No assemblies specified.");
129
130                 Run (opts);
131                 opts.name = ""; // remove warning about unused member
132         }
133
134         static ExceptionLocations ParseExceptionLocations (string s)
135         {
136                 ExceptionLocations loc = ExceptionLocations.Member;
137                 if (s == null)
138                         return loc;
139                 foreach (var type in s.Split (',')) {
140                         switch (type) {
141                                 case "asm":     loc |= ExceptionLocations.Assembly; break;
142                                 case "depasm":  loc |= ExceptionLocations.DependentAssemblies; break;
143                                 case "all":     loc = ExceptionLocations.All; break;
144                                 default:        throw new NotSupportedException ("Unsupported --exceptions value: " + type);
145                         }
146                 }
147                 return loc;
148         }
149
150         static void Run (MDocUpdaterOptions opts)
151         {
152                 nooverrides = !opts.overrides;
153                 delete = opts.delete;
154                 ignoremembers = opts.ignoremembers;
155                 name = opts.name;
156                 pretty = opts.pretty;
157                 since = opts.since;
158                 show_exceptions = opts.show_exceptions;
159                 exceptions = opts.exceptions;
160                 no_assembly_versions = opts.no_assembly_versions;
161
162                 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
163                 
164                 if (opts.path == null)
165                         throw new InvalidOperationException("The --out option is required.");
166                 
167                 srcPath = opts.path;
168
169                 if (opts.type != null && opts.type.Count > 0 && opts.@namespace != null)
170                         throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
171                 
172                 if (opts.assembly == null)
173                         throw new InvalidOperationException("The assembly option is required.");
174                         
175                 assemblies = opts.assembly.Select (a => LoadAssembly (a)).ToList ();
176
177                 if (opts.import != null && ecmadocs == null && slashdocs == null) {
178                         try {
179                                 XmlReader r = new XmlTextReader (opts.import);
180                                 if (r.Read ()) {
181                                         while (r.NodeType != XmlNodeType.Element) {
182                                                 if (!r.Read ())
183                                                         throw new Exception ("Unable to read XML file: " + 
184                                                                         opts.import);
185                                         }
186                                         if (r.LocalName == "doc") {
187                                                 slashdocs = new XmlDocument();
188                                                 slashdocs.Load (opts.import);
189                                         }
190                                         else if (r.LocalName == "Libraries") {
191                                                 ecmadocs = new XmlTextReader (opts.import);
192                                         }
193                                         else
194                                                 throw new Exception ("Unsupported XML format within " + opts.import);
195                                 }
196                                 r.Close ();
197                         } catch (Exception e) {
198                                 Error ("Could not load XML file: {0}", e.Message);
199                                 Environment.ExitCode = 1;
200                                 return;
201                         }
202                 }
203                 
204                 // PERFORM THE UPDATES
205                 
206                 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
207                 if (opts.type != null && opts.type.Count > 0)
208                         DoUpdateTypes(opts.path, opts.type, dest_dir);
209                 else if (opts.@namespace != null)
210                         DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
211                                         Path.Combine (dest_dir, opts.@namespace));
212                 else
213                         DoUpdateAssemblies(opts.path, dest_dir);
214
215                 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
216         }
217
218         private static new void Error (string format, params object[] args)
219         {
220                 Console.Error.Write ("mdoc: ");
221                 Console.Error.WriteLine (format, args);
222         }
223         
224         private static AssemblyDefinition LoadAssembly (string name)
225         {
226                 AssemblyDefinition assembly = null;
227                 try {
228                         assembly = AssemblyFactory.GetAssembly (name);
229                 } catch (System.IO.FileNotFoundException) { }
230
231                 if (assembly == null)
232                         throw new InvalidOperationException("Assembly " + name + " not found.");
233
234                 var r = assembly.Resolver as BaseAssemblyResolver;
235                 if (r != null && name.Contains (Path.DirectorySeparatorChar)) {
236                         r.AddSearchDirectory (Path.GetDirectoryName (name));
237                 }
238                 return assembly;
239         }
240
241         private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
242                 OrderTypeAttributes (element);
243                 XmlTextWriter writer = new XmlTextWriter(output);
244                 writer.Formatting = Formatting.Indented;
245                 writer.Indentation = 2;
246                 writer.IndentChar = ' ';
247                 element.WriteTo(writer);
248                 output.WriteLine();     
249         }
250
251         private static void OrderTypeAttributes (XmlElement e)
252         {
253                 foreach (XmlElement type in e.SelectNodes ("//Type")) {
254                         OrderTypeAttributes (type.Attributes);
255                 }
256         }
257
258         static readonly string[] TypeAttributeOrder = {
259                 "Name", "FullName", "FullNameSP", "Maintainer"
260         };
261
262         private static void OrderTypeAttributes (XmlAttributeCollection c)
263         {
264                 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
265                 for (int i = 0; i < c.Count; ++i) {
266                         XmlAttribute a = c [i];
267                         for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
268                                 if (a.Name == TypeAttributeOrder [j]) {
269                                         attrs [j] = a;
270                                         break;
271                                 }
272                         }
273                 }
274                 for (int i = attrs.Length-1; i >= 0; --i) {
275                         XmlAttribute n = attrs [i];
276                         if (n == null)
277                                 continue;
278                         XmlAttribute r = null;
279                         for (int j = i+1; j < attrs.Length; ++j) {
280                                 if (attrs [j] != null) {
281                                         r = attrs [j];
282                                         break;
283                                 }
284                         }
285                         if (r == null)
286                                 continue;
287                         c.Remove (n);
288                         c.InsertBefore (n, r);
289                 }
290         }
291         
292         private static XmlDocument CreateIndexStub() {
293                 XmlDocument index = new XmlDocument();
294
295                 XmlElement index_root = index.CreateElement("Overview");
296                 index.AppendChild(index_root);
297
298                 if (assemblies.Count == 0)
299                         throw new Exception ("No assembly");
300
301                 XmlElement index_assemblies = index.CreateElement("Assemblies");
302                 index_root.AppendChild(index_assemblies);
303
304                 XmlElement index_remarks = index.CreateElement("Remarks");
305                 index_remarks.InnerText = "To be added.";
306                 index_root.AppendChild(index_remarks);
307
308                 XmlElement index_copyright = index.CreateElement("Copyright");
309                 index_copyright.InnerText = "To be added.";
310                 index_root.AppendChild(index_copyright);
311
312                 XmlElement index_types = index.CreateElement("Types");
313                 index_root.AppendChild(index_types);
314                 
315                 return index;
316         }
317         
318         private static void WriteNamespaceStub(string ns, string outdir) {
319                 XmlDocument index = new XmlDocument();
320
321                 XmlElement index_root = index.CreateElement("Namespace");
322                 index.AppendChild(index_root);
323                 
324                 index_root.SetAttribute("Name", ns);
325
326                 XmlElement index_docs = index.CreateElement("Docs");
327                 index_root.AppendChild(index_docs);
328
329                 XmlElement index_summary = index.CreateElement("summary");
330                 index_summary.InnerText = "To be added.";
331                 index_docs.AppendChild(index_summary);
332
333                 XmlElement index_remarks = index.CreateElement("remarks");
334                 index_remarks.InnerText = "To be added.";
335                 index_docs.AppendChild(index_remarks);
336
337                 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml",  FileMode.CreateNew)) {
338                         WriteXml(index.DocumentElement, writer);
339                 }
340         }
341
342         public static void DoUpdateTypes (string basepath, List<string> typenames, string dest)
343         {
344                 var found = new HashSet<string> ();
345                 foreach (AssemblyDefinition assembly in assemblies) {
346                         foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
347                                 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
348                                 if (relpath != null)
349                                         found.Add (docsTypeInfo.Type.FullName);
350                         }
351                 }
352                 var notFound = from n in typenames where !found.Contains (n) select n;
353                 if (notFound.Any ())
354                         throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
355         }
356
357         public static string DoUpdateType(TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
358         {
359                 if (type.Namespace == null)
360                         Error ("warning: The type `{0}' is in the root namespace.  This may cause problems with display within monodoc.",
361                                         type.FullName);
362                 if (!IsPublic (type))
363                         return null;
364                 
365                 // Must get the A+B form of the type name.
366                 string typename = GetTypeFileName(type);
367                 
368                 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
369                 string typefile = Path.Combine (basepath, reltypefile);
370                 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
371
372                 string output = null;
373                 if (dest == null) {
374                         output = typefile;
375                 } else if (dest == "-") {
376                         output = null;
377                 } else {
378                         output = Path.Combine (dest, reltypefile);
379                 }       
380
381                 if (file.Exists) {
382                         // Update
383                         XmlDocument basefile = new XmlDocument();
384                         if (!pretty) basefile.PreserveWhitespace = true;
385                         try {
386                                 basefile.Load(typefile);
387                         } catch (Exception e) {
388                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
389                         }
390                         
391                         DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
392                 } else {
393                         // Stub
394                         XmlElement td = StubType(type, output, ecmaDocsType);
395                         if (td == null)
396                                 return null;
397                         
398                         System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
399                         if (!dir.Exists) {
400                                 dir.Create();
401                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
402                         }
403                 }
404                 return reltypefile;
405         }
406
407         public static void DoUpdateNS(string ns, string nspath, string outpath) {
408                 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
409                 AssemblyDefinition                  assembly = assemblies [0];
410
411                 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
412                         XmlDocument basefile = new XmlDocument();
413                         if (!pretty) basefile.PreserveWhitespace = true;
414                         string typefile = Path.Combine(nspath, file.Name);
415                         try {
416                                 basefile.Load(typefile);
417                         } catch (Exception e) {
418                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
419                         }
420
421                         string typename = 
422                                 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
423                         TypeDefinition type = assembly.GetType(typename);
424                         if (type == null) {
425                                 Error ("Type no longer in assembly: " + typename);
426                                 continue;
427                         }                       
428
429                         seenTypes[type] = seenTypes;
430                         DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
431                 }
432                 
433                 // Stub types not in the directory
434                 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
435                         TypeDefinition type = docsTypeInfo.Type;
436                         if (type.Namespace != ns || seenTypes.ContainsKey(type))
437                                 continue;
438
439                         XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
440                         if (td == null) continue;
441                 }
442         }
443         
444         private static string GetTypeFileName (TypeReference type)
445         {
446                 return filenameFormatter.GetName (type);
447         }
448
449         public static string GetTypeFileName (string typename)
450         {
451                 StringBuilder filename = new StringBuilder (typename.Length);
452                 int numArgs = 0;
453                 int numLt = 0;
454                 bool copy = true;
455                 for (int i = 0; i < typename.Length; ++i) {
456                         char c = typename [i];
457                         switch (c) {
458                                 case '<':
459                                         copy = false;
460                                         ++numLt;
461                                         break;
462                                 case '>':
463                                         --numLt;
464                                         if (numLt == 0) {
465                                                 filename.Append ('`').Append ((numArgs+1).ToString());
466                                                 numArgs = 0;
467                                                 copy = true;
468                                         }
469                                         break;
470                                 case ',':
471                                         if (numLt == 1)
472                                                 ++numArgs;
473                                         break;
474                                 default:
475                                         if (copy)
476                                                 filename.Append (c);
477                                         break;
478                         }
479                 }
480                 return filename.ToString ();
481         }
482
483         private static void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
484         {
485                 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
486                 index_assembly.SetAttribute ("Name", assembly.Name.Name);
487                 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
488                 MakeAttributes (index_assembly, assembly.CustomAttributes, true);
489                 parent.AppendChild(index_assembly);
490         }
491
492         private static void DoUpdateAssemblies (string source, string dest) 
493         {
494                 string indexfile = dest + "/index.xml";
495                 XmlDocument index;
496                 if (System.IO.File.Exists(indexfile)) {
497                         index = new XmlDocument();
498                         index.Load(indexfile);
499
500                         // Format change
501                         ClearElement(index.DocumentElement, "Assembly");
502                         ClearElement(index.DocumentElement, "Attributes");
503                 } else {
504                         index = CreateIndexStub();
505                 }
506                 
507                 if (name == null) {
508                         string defaultTitle = "Untitled";
509                         if (assemblies.Count == 1)
510                                 defaultTitle = assemblies[0].Name.Name;
511                         WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
512                 } else {
513                         WriteElementText(index.DocumentElement, "Title", name);
514                 }
515                 
516                 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
517                 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
518                 index_assemblies.RemoveAll ();
519
520
521                 HashSet<string> goodfiles = new HashSet<string> ();
522
523                 foreach (AssemblyDefinition assm in assemblies) {
524                         AddIndexAssembly (assm, index_assemblies);
525                         DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
526                 }
527
528                 SortIndexEntries (index_types);
529                 
530                 CleanupFiles (dest, goodfiles);
531                 CleanupIndexTypes (index_types, goodfiles);
532                 CleanupExtensions (index_types);
533
534                 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
535                         WriteXml(index.DocumentElement, writer);
536         }
537                 
538         private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
539
540         private static void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles) 
541         {
542                 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
543                         TypeDefinition type = docTypeInfo.Type;
544                         string typename = GetTypeFileName(type);
545                         if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
546                                 continue;
547
548                         string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
549                         if (reltypepath == null)
550                                 continue;
551                         
552                         // Add namespace and type nodes into the index file as needed
553                         string ns = DocUtils.GetNamespace (type);
554                         XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
555                         if (nsnode == null) {
556                                 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
557                                 nsnode.SetAttribute ("Name", ns);
558                                 index_types.AppendChild(nsnode);
559                         }
560                         string doc_typename = GetDocTypeName (type);
561                         XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
562                         if (typenode == null) {
563                                 typenode = index_types.OwnerDocument.CreateElement("Type");
564                                 typenode.SetAttribute("Name", typename);
565                                 nsnode.AppendChild(typenode);
566                         }
567                         if (typename != doc_typename)
568                                 typenode.SetAttribute("DisplayName", doc_typename);
569                         else
570                                 typenode.RemoveAttribute("DisplayName");
571                         typenode.SetAttribute ("Kind", GetTypeKind (type));
572                                 
573                         // Ensure the namespace index file exists
574                         string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
575                         string nsdoc  = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
576                         if (File.Exists (onsdoc)) {
577                                 File.Move (onsdoc, nsdoc);
578                         }
579
580                         if (!File.Exists (nsdoc)) {
581                                 Console.WriteLine("New Namespace File: " + type.Namespace);
582                                 WriteNamespaceStub(type.Namespace, dest);
583                         }
584
585                         goodfiles.Add (reltypepath);
586                 }
587         }
588
589         class DocsTypeInfo {
590                 public TypeDefinition Type;
591                 public XmlReader EcmaDocs;
592
593                 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
594                 {
595                         this.Type = type;
596                         this.EcmaDocs = docs;
597                 }
598         }
599
600         static IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
601         {
602                 HashSet<string> seen = null;
603                 if (forTypes != null)
604                         forTypes.Sort ();
605                 if (ecmadocs != null) {
606                         seen = new HashSet<string> ();
607                         int typeDepth = -1;
608                         while (ecmadocs.Read ()) {
609                                 switch (ecmadocs.Name) {
610                                         case "Type": {
611                                                 if (typeDepth == -1)
612                                                         typeDepth = ecmadocs.Depth;
613                                                 if (ecmadocs.NodeType != XmlNodeType.Element)
614                                                         continue;
615                                                 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
616                                                         continue;
617                                                 string typename = ecmadocs.GetAttribute ("FullName");
618                                                 string typename2 = GetTypeFileName (typename);
619                                                 if (forTypes != null && 
620                                                                 forTypes.BinarySearch (typename) < 0 &&
621                                                                 typename != typename2 &&
622                                                                 forTypes.BinarySearch (typename2) < 0)
623                                                         continue;
624                                                 TypeDefinition t;
625                                                 if ((t = assembly.GetType (typename)) == null && 
626                                                                 (t = assembly.GetType (typename2)) == null)
627                                                         continue;
628                                                 seen.Add (typename);
629                                                 if (typename != typename2)
630                                                         seen.Add (typename2);
631                                                 Console.WriteLine ("  Import: {0}", t.FullName);
632                                                 yield return new DocsTypeInfo (t, ecmadocs);
633                                                 break;
634                                         }
635                                         default:
636                                                 break;
637                                 }
638                         }
639                 }
640                 foreach (TypeDefinition type in assembly.GetTypes()) {
641                         if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
642                                 continue;
643                         if (seen != null && seen.Contains (type.FullName))
644                                 continue;
645                         yield return new DocsTypeInfo (type, null);
646                         foreach (TypeDefinition nested in type.NestedTypes)
647                                 yield return new DocsTypeInfo (nested, null);
648                 }
649         }
650
651         private static void SortIndexEntries (XmlElement indexTypes)
652         {
653                 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
654                 XmlNodeComparer c = new AttributeNameComparer ();
655                 SortXmlNodes (indexTypes, namespaces, c);
656
657                 for (int i = 0; i < namespaces.Count; ++i)
658                         SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
659         }
660
661         private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
662         {
663                 MyXmlNodeList l = new MyXmlNodeList (children.Count);
664                 for (int i = 0; i < children.Count; ++i)
665                         l.Add (children [i]);
666                 l.Sort (comparer);
667                 for (int i = l.Count - 1; i > 0; --i) {
668                         parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
669                 }
670         }
671
672         abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
673         {
674                 public abstract int Compare (XmlNode x, XmlNode y);
675
676                 public int Compare (object x, object y)
677                 {
678                         return Compare ((XmlNode) x, (XmlNode) y);
679                 }
680         }
681
682         class AttributeNameComparer : XmlNodeComparer {
683                 string attribute;
684
685                 public AttributeNameComparer ()
686                         : this ("Name")
687                 {
688                 }
689
690                 public AttributeNameComparer (string attribute)
691                 {
692                         this.attribute = attribute;
693                 }
694
695                 public override int Compare (XmlNode x, XmlNode y)
696                 {
697                         return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
698                 }
699         }
700         
701         class VersionComparer : XmlNodeComparer {
702                 public override int Compare (XmlNode x, XmlNode y)
703                 {
704                         // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
705                         string a = GetVersion (x.InnerText);
706                         string b = GetVersion (y.InnerText);
707                         return new Version (a).CompareTo (new Version (b));
708                 }
709
710                 static string GetVersion (string v)
711                 {
712                         int n = v.IndexOf ("x");
713                         if (n < 0)
714                                 return v;
715                         return v.Substring (0, n-1);
716                 }
717         }
718
719         private static string GetTypeKind (TypeDefinition type)
720         {
721                 if (type.IsEnum)
722                         return "Enumeration";
723                 if (type.IsValueType)
724                         return "Structure";
725                 if (type.IsInterface)
726                         return "Interface";
727                 if (DocUtils.IsDelegate (type))
728                         return "Delegate";
729                 if (type.IsClass || type.FullName == "System.Enum") // FIXME
730                         return "Class";
731                 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
732         }
733
734         private static bool IsPublic (TypeDefinition type)
735         {
736                 TypeDefinition decl = type;
737                 while (decl != null) {
738                         if (!(decl.IsPublic || decl.IsNestedPublic)) {
739                                 return false;
740                         }
741                         decl = (TypeDefinition) decl.DeclaringType;
742                 }
743                 return true;
744         }
745
746         private static void CleanupFiles (string dest, HashSet<string> goodfiles)
747         {
748                 // Look for files that no longer correspond to types
749                 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
750                         foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
751                                 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
752                                 if (!goodfiles.Contains (relTypeFile)) {
753                                         XmlDocument doc = new XmlDocument ();
754                                         doc.Load (typefile.FullName);
755                                         XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
756                                         if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
757                                                 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
758                                                         WriteXml(doc.DocumentElement, writer);
759                                                 goodfiles.Add (relTypeFile);
760                                                 continue;
761                                         }
762                                         string newname = typefile.FullName + ".remove";
763                                         try { System.IO.File.Delete(newname); } catch (Exception) { }
764                                         try { typefile.MoveTo(newname); } catch (Exception) { }
765                                         Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
766                                 }
767                         }
768                 }
769         }
770
771         private static TextWriter OpenWrite (string path, FileMode mode)
772         {
773                 return new StreamWriter (
774                         new FileStream (path, mode),
775                         new UTF8Encoding (false)
776                 );
777         }
778
779         private static string[] GetAssemblyVersions ()
780         {
781                 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
782         }
783
784         private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
785         {
786                 // Look for type nodes that no longer correspond to types
787                 MyXmlNodeList remove = new MyXmlNodeList ();
788                 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
789                         string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
790                         if (!goodfiles.Contains (fulltypename)) {
791                                 remove.Add (typenode);
792                         }
793                 }
794                 foreach (XmlNode n in remove)
795                         n.ParentNode.RemoveChild (n);
796         }
797
798         private static void CleanupExtensions (XmlElement index_types)
799         {
800                 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
801                 if (extensionMethods.Count == 0) {
802                         if (e == null)
803                                 return;
804                         index_types.RemoveChild (e);
805                         return;
806                 }
807                 if (e == null) {
808                         e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
809                         index_types.SelectSingleNode ("/Overview").AppendChild (e);
810                 }
811                 else
812                         e.RemoveAll ();
813                 extensionMethods.Sort (DefaultExtensionMethodComparer);
814                 foreach (XmlNode m in extensionMethods) {
815                         e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
816                 }
817         }
818
819         class ExtensionMethodComparer : XmlNodeComparer {
820                 public override int Compare (XmlNode x, XmlNode y)
821                 {
822                         XmlNode xLink = x.SelectSingleNode ("Member/Link");
823                         XmlNode yLink = y.SelectSingleNode ("Member/Link");
824
825                         int n = xLink.Attributes ["Type"].Value.CompareTo (
826                                         yLink.Attributes ["Type"].Value);
827                         if (n != 0)
828                                 return n;
829                         n = xLink.Attributes ["Member"].Value.CompareTo (
830                                         yLink.Attributes ["Member"].Value);
831                         if (n == 0 && !object.ReferenceEquals (x, y))
832                                 throw new InvalidOperationException ("Duplicate extension method found!");
833                         return n;
834                 }
835         }
836
837         static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
838                 
839         public static void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
840         {
841                 Console.WriteLine(message + ": " + type.FullName);
842                 
843                 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
844
845                 // Update type metadata
846                 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
847
848                 if (ecmaDocsType != null) {
849                         while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
850                                 // do nothing
851                         }
852                         if (ecmaDocsType.IsEmptyElement)
853                                 ecmaDocsType = null;
854                 }
855
856                 // Update existing members.  Delete member nodes that no longer should be there,
857                 // and remember what members are already documented so we don't add them again.
858                 if (!ignoremembers) {
859                         MyXmlNodeList todelete = new MyXmlNodeList ();
860                         foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
861                                 XmlElement oldmember  = info.Node;
862                                 IMemberReference oldmember2 = info.Member;
863                                 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
864
865                                 // Interface implementations and overrides are deleted from the docs
866                                 // unless the overrides option is given.
867                                 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
868                                         oldmember2 = null;
869                                 
870                                 // Deleted (or signature changed)
871                                 if (oldmember2 == null) {
872                                         if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
873                                                 continue;
874                                         DeleteMember ("Member Removed", output, oldmember, todelete);
875                                         continue;
876                                 }
877                                 
878                                 // Duplicated
879                                 if (seenmembers.ContainsKey (sig)) {
880                                         if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
881                                                 // ignore, already seen
882                                         }
883                                         else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
884                                                 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
885                                         else
886                                                 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
887                                         continue;
888                                 }
889                                 
890                                 // Update signature information
891                                 UpdateMember(info);
892                                 
893                                 seenmembers.Add (sig, oldmember);
894                         }
895                         foreach (XmlElement oldmember in todelete)
896                                 oldmember.ParentNode.RemoveChild (oldmember);
897                 }
898                 
899                 if (!DocUtils.IsDelegate (type) && !ignoremembers) {
900                         XmlNode members = WriteElement (basefile.DocumentElement, "Members");
901                         foreach (IMemberReference m in type.GetMembers()) {
902                                 if (m is TypeDefinition) continue;
903                                 
904                                 string sig = MakeMemberSignature(m);
905                                 if (sig == null) continue;
906                                 if (seenmembers.ContainsKey(sig)) continue;
907                                 
908                                 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
909                                 // are not added in.
910                                 if (!IsNew(m)) continue;
911                                 
912                                 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
913                                 if (mm == null) continue;
914                                 members.AppendChild( mm );
915         
916                                 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
917                                 additions++;
918                         }
919                 }
920                 
921                 // Import code snippets from files
922                 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
923                         if (!(code is XmlElement)) continue;
924                         string file = ((XmlElement)code).GetAttribute("src");
925                         string lang = ((XmlElement)code).GetAttribute("lang");
926                         if (file != "") {
927                                 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
928                                 if (src != null)
929                                         code.InnerText = src;
930                         }
931                 }
932
933                 if (insertSince && since != null) {
934                         XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
935                         docs.AppendChild (CreateSinceNode (basefile));
936                 }
937
938                 do {
939                         XmlElement d = basefile.DocumentElement ["Docs"];
940                         XmlElement m = basefile.DocumentElement ["Members"];
941                         if (d != null && m != null)
942                                 basefile.DocumentElement.InsertBefore (
943                                                 basefile.DocumentElement.RemoveChild (d), m);
944                         SortTypeMembers (m);
945                 } while (false);
946
947                 System.IO.TextWriter writer;
948                 if (output == null)
949                         writer = Console.Out;
950                 else {
951                         FileInfo file = new FileInfo (output);
952                         if (!file.Directory.Exists) {
953                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
954                                 file.Directory.Create ();
955                         }
956                         writer = OpenWrite (output, FileMode.Create);
957                 }
958
959                 using (writer)
960                         WriteXml(basefile.DocumentElement, writer);
961         }
962
963         private static string GetCodeSource (string lang, string file)
964         {
965                 int anchorStart;
966                 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
967                         // Grab the specified region
968                         string region = "#region " + file.Substring (anchorStart + 4);
969                         file          = file.Substring (0, anchorStart + 3);
970                         try {
971                                 using (StreamReader reader = new StreamReader (file)) {
972                                         string line;
973                                         StringBuilder src = new StringBuilder ();
974                                         int indent = -1;
975                                         while ((line = reader.ReadLine ()) != null) {
976                                                 if (line.Trim() == region) {
977                                                         indent = line.IndexOf (region);
978                                                         continue;
979                                                 }
980                                                 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
981                                                         break;
982                                                 }
983                                                 if (indent >= 0)
984                                                         src.Append (
985                                                                         (line.Length > 0 ? line.Substring (indent) : string.Empty) +
986                                                                         "\n");
987                                         }
988                                         return src.ToString ();
989                                 }
990                         } catch (Exception e) {
991                                 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
992                                                 file, region, show_exceptions ? e.ToString () : e.Message);
993                                 return null;
994                         }
995                 }
996                 try {
997                         using (StreamReader reader = new StreamReader (file))
998                                 return reader.ReadToEnd ();
999                 } catch (Exception e) {
1000                         Error ("Could not load <code/> file '" + file + "': " + e.Message);
1001                 }
1002                 return null;
1003         }
1004
1005         private static IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
1006         {
1007                 if (ecmaDocsMembers != null) {
1008                         int membersDepth = ecmaDocsMembers.Depth;
1009                         bool go = true;
1010                         while (go && ecmaDocsMembers.Read ()) {
1011                                 switch (ecmaDocsMembers.Name) {
1012                                         case "Member": {
1013                                                 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1014                                                         continue;
1015                                                 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1016                                                 string xp = GetXPathForMember (dm);
1017                                                 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1018                                                 IMemberReference m;
1019                                                 if (oldmember == null) {
1020                                                         m = GetMember (type, dm);
1021                                                         if (m == null) {
1022                                                                 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1023                                                                                 type.FullName, dm.MemberSignatures ["C#"]);
1024                                                                                 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1025                                                                 continue;
1026                                                         }
1027                                                         // oldmember lookup may have failed due to type parameter renames.
1028                                                         // Try again.
1029                                                         oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1030                                                         if (oldmember == null) {
1031                                                                 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1032                                                                 oldmember = basefile.CreateElement ("Member");
1033                                                                 oldmember.SetAttribute ("MemberName", dm.MemberName);
1034                                                                 members.AppendChild (oldmember);
1035                                                                 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1036                                                                         XmlElement ms = basefile.CreateElement ("MemberSignature");
1037                                                                         ms.SetAttribute ("Language", key);
1038                                                                         ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1039                                                                         oldmember.AppendChild (ms);
1040                                                                 }
1041                                                                 oldmember.SetAttribute ("__monodocer-seen__", "true");
1042                                                                 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1043                                                                 additions++;
1044                                                         }
1045                                                 }
1046                                                 else {
1047                                                         m = GetMember (type, new DocumentationMember (oldmember));
1048                                                         if (m == null) {
1049                                                                 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1050                                                                                 type.FullName, dm.MemberSignatures ["C#"]);
1051                                                                 continue;
1052                                                         }
1053                                                         oldmember.SetAttribute ("__monodocer-seen__", "true");
1054                                                 }
1055                                                 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1056                                                 if (ecmaDocsMembers.Name != "Docs")
1057                                                         throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1058                                                 node.EcmaDocs = ecmaDocsMembers;
1059                                                 yield return node;
1060                                                 break;
1061                                         }
1062                                         case "Members":
1063                                                 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1064                                                         go = false;
1065                                                 }
1066                                                 break;
1067                                 }
1068                         }
1069                 }
1070                 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1071                         if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1072                                 oldmember.RemoveAttribute ("__monodocer-seen__");
1073                                 continue;
1074                         }
1075                         IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1076                         if (m == null) {
1077                                 yield return new DocsNodeInfo (oldmember);
1078                         }
1079                         else {
1080                                 yield return new DocsNodeInfo (oldmember, m);
1081                         }
1082                 }
1083         }
1084
1085         static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1086         {
1087                 string format = output != null
1088                         ? "{0}: File='{1}'; Signature='{4}'"
1089                         : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1090                 Error (format,
1091                                 reason, 
1092                                 output,
1093                                 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1094                                 member.Attributes ["MemberName"].Value, 
1095                                 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1096                 if (!delete && MemberDocsHaveUserContent (member)) {
1097                         Error ("Member deletions must be enabled with the --delete option.");
1098                 } else {
1099                         todelete.Add (member);
1100                         deletions++;
1101                 }
1102         }
1103
1104         class MemberComparer : XmlNodeComparer {
1105                 public override int Compare (XmlNode x, XmlNode y)
1106                 {
1107                         int r;
1108                         string xMemberName = x.Attributes ["MemberName"].Value;
1109                         string yMemberName = y.Attributes ["MemberName"].Value;
1110
1111                         // generic methods *end* with '>'
1112                         // it's possible for explicitly implemented generic interfaces to
1113                         // contain <...> without being a generic method
1114                         if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1115                                         (r = xMemberName.CompareTo (yMemberName)) != 0)
1116                                 return r;
1117
1118                         int lt;
1119                         if ((lt = xMemberName.IndexOf ("<")) >= 0)
1120                                 xMemberName = xMemberName.Substring (0, lt);
1121                         if ((lt = yMemberName.IndexOf ("<")) >= 0)
1122                                 yMemberName = yMemberName.Substring (0, lt);
1123                         if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1124                                 return r;
1125
1126                         // if @MemberName matches, then it's either two different types of
1127                         // members sharing the same name, e.g. field & property, or it's an
1128                         // overloaded method.
1129                         // for different type, sort based on MemberType value.
1130                         r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1131                                         y.SelectSingleNode ("MemberType").InnerText);
1132                         if (r != 0)
1133                                 return r;
1134
1135                         // same type -- must be an overloaded method.  Sort based on type 
1136                         // parameter count, then parameter count, then by the parameter 
1137                         // type names.
1138                         XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1139                         XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1140                         if (xTypeParams.Count != yTypeParams.Count)
1141                                 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1142                         for (int i = 0; i < xTypeParams.Count; ++i) {
1143                                 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1144                                                 yTypeParams [i].Attributes ["Name"].Value);
1145                                 if (r != 0)
1146                                         return r;
1147                         }
1148
1149                         XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1150                         XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1151                         if (xParams.Count != yParams.Count)
1152                                 return xParams.Count <= yParams.Count ? -1 : 1;
1153                         for (int i = 0; i < xParams.Count; ++i) {
1154                                 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1155                                                 yParams [i].Attributes ["Type"].Value);
1156                                 if (r != 0)
1157                                         return r;
1158                         }
1159                         // all parameters match, but return value might not match if it was
1160                         // changed between one version and another.
1161                         XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1162                         XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1163                         if (xReturn != null && yReturn != null) {
1164                                 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1165                                 if (r != 0)
1166                                         return r;
1167                         }
1168
1169                         return 0;
1170                 }
1171         }
1172
1173         static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1174
1175         private static void SortTypeMembers (XmlNode members)
1176         {
1177                 if (members == null)
1178                         return;
1179                 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1180         }
1181         
1182         private static bool MemberDocsHaveUserContent (XmlNode e)
1183         {
1184                 e = (XmlElement)e.SelectSingleNode("Docs");
1185                 if (e == null) return false;
1186                 foreach (XmlElement d in e.SelectNodes("*"))
1187                         if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1188                                 return true;
1189                 return false;
1190         }
1191         
1192         private static bool IsNew (IMemberReference m)
1193         {
1194                 if (!nooverrides) return true;
1195                 if (m is MethodDefinition && !IsNew ((MethodDefinition)m)) return false;
1196                 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).GetMethod)) return false;
1197                 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).SetMethod)) return false;
1198                 if (m is EventDefinition && !IsNew (((EventDefinition)m).AddMethod)) return false;
1199                 if (m is EventDefinition && !IsNew (((EventDefinition)m).InvokeMethod)) return false;
1200                 if (m is EventDefinition && !IsNew (((EventDefinition)m).RemoveMethod)) return false;
1201                 return true;
1202         }
1203         
1204         private static bool IsNew (MethodDefinition m)
1205         {
1206                 if (m == null)
1207                         return true;
1208                 return m.IsNewSlot;
1209         }
1210
1211         // UPDATE HELPER FUNCTIONS
1212
1213         private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1214         {
1215                 string membertype = member.MemberType;
1216                 
1217                 string returntype = member.ReturnType;
1218                 
1219                 string docName = member.MemberName;
1220                 string[] docTypeParams = GetTypeParameters (docName);
1221
1222                 // Loop through all members in this type with the same name
1223                 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1224                         if (mi is TypeDefinition) continue;
1225                         if (GetMemberType(mi) != membertype) continue;
1226
1227                         string sig = MakeMemberSignature(mi);
1228                         if (sig == null) continue; // not publicly visible
1229
1230                         ParameterDefinitionCollection pis = null;
1231                         string[] typeParams = null;
1232                         if (mi is MethodDefinition) {
1233                                 MethodDefinition mb = (MethodDefinition) mi;
1234                                 pis = mb.Parameters;
1235                                 if (docTypeParams != null && mb.IsGenericMethod ()) {
1236                                         GenericParameterCollection args = mb.GenericParameters;
1237                                         if (args.Count == docTypeParams.Length) {
1238                                                 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1239                                         }
1240                                 }
1241                         }
1242                         else if (mi is PropertyDefinition)
1243                                 pis = ((PropertyDefinition)mi).Parameters;
1244                         
1245                         int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1246                         int pcount = pis == null ? 0 : pis.Count;
1247                         if (mcount != pcount)
1248                                 continue;
1249
1250                         MethodDefinition mDef = mi as MethodDefinition;
1251                         if (mDef != null && !mDef.IsConstructor) {
1252                                 // Casting operators can overload based on return type.
1253                                 if (returntype != GetReplacedString (
1254                                                         GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType), 
1255                                                         typeParams, docTypeParams)) {
1256                                         continue;
1257                                 }
1258                         }
1259
1260                         if (pcount == 0)
1261                                 return mi;
1262                         bool good = true;
1263                         for (int i = 0; i < pis.Count; i++) {
1264                                 string paramType = GetReplacedString (
1265                                         GetDocParameterType (pis [i].ParameterType),
1266                                         typeParams, docTypeParams);
1267                                 if (paramType != (string) member.Parameters [i]) {
1268                                         good = false;
1269                                         break;
1270                                 }
1271                         }
1272                         if (!good) continue;
1273
1274                         return mi;
1275                 }
1276                 
1277                 return null;
1278         }
1279
1280         private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1281         {
1282                 // need to worry about 4 forms of //@MemberName values:
1283                 //  1. "Normal" (non-generic) member names: GetEnumerator
1284                 //    - Lookup as-is.
1285                 //  2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1286                 //    - try as-is, and try type.member (due to "kludge" for property
1287                 //      support.
1288                 //  3. "Normal" Generic member names: Sort<T> (CSC)
1289                 //    - need to remove generic parameters --> "Sort"
1290                 //  4. Explicitly-implemented interface members for generic interfaces: 
1291                 //    -- System.Collections.Generic.IEnumerable<T>.Current
1292                 //    - Try as-is, and try type.member, *keeping* the generic parameters.
1293                 //     --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1294                 //  5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1295                 //    'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1296                 //    this as (1) or (2).
1297                 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1298                         // Cases 1 & 2
1299                         foreach (IMemberReference mi in type.GetMembers (docName))
1300                                 yield return mi;
1301                         if (CountChars (docName, '.') > 0)
1302                                 // might be a property; try only type.member instead of
1303                                 // namespace.type.member.
1304                                 foreach (IMemberReference mi in 
1305                                                 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1306                                         yield return mi;
1307                         yield break;
1308                 }
1309                 // cases 3 & 4
1310                 int numLt = 0;
1311                 int numDot = 0;
1312                 int startLt, startType, startMethod;
1313                 startLt = startType = startMethod = -1;
1314                 for (int i = 0; i < docName.Length; ++i) {
1315                         switch (docName [i]) {
1316                                 case '<':
1317                                         if (numLt == 0) {
1318                                                 startLt = i;
1319                                         }
1320                                         ++numLt;
1321                                         break;
1322                                 case '>':
1323                                         --numLt;
1324                                         if (numLt == 0 && (i + 1) < docName.Length)
1325                                                 // there's another character in docName, so this <...> sequence is
1326                                                 // probably part of a generic type -- case 4.
1327                                                 startLt = -1;
1328                                         break;
1329                                 case '.':
1330                                         startType = startMethod;
1331                                         startMethod = i;
1332                                         ++numDot;
1333                                         break;
1334                         }
1335                 }
1336                 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1337                 // case 3
1338                 foreach (IMemberReference mi in type.GetMembers (refName))
1339                         yield return mi;
1340
1341                 // case 4
1342                 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1343                         yield return mi;
1344
1345                 // If we _still_ haven't found it, we've hit another generic naming issue:
1346                 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1347                 // explicitly-implemented METHOD names (not properties), e.g. 
1348                 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1349                 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1350                 // which the XML docs will contain.
1351                 //
1352                 // Alas, we can't derive the Mono name from docName, so we need to iterate
1353                 // over all member names, convert them into CSC format, and compare... :-(
1354                 if (numDot == 0)
1355                         yield break;
1356                 foreach (IMemberReference mi in type.GetMembers ()) {
1357                         if (GetMemberName (mi) == docName)
1358                                 yield return mi;
1359                 }
1360         }
1361
1362         static string[] GetTypeParameters (string docName)
1363         {
1364                 if (docName [docName.Length-1] != '>')
1365                         return null;
1366                 StringList types = new StringList ();
1367                 int endToken = docName.Length-2;
1368                 int i = docName.Length-2;
1369                 do {
1370                         if (docName [i] == ',' || docName [i] == '<') {
1371                                 types.Add (docName.Substring (i + 1, endToken - i));
1372                                 endToken = i-1;
1373                         }
1374                         if (docName [i] == '<')
1375                                 break;
1376                 } while (--i >= 0);
1377
1378                 types.Reverse ();
1379                 return types.ToArray ();
1380         }
1381
1382         static string GetReplacedString (string typeName, string[] from, string[] to)
1383         {
1384                 if (from == null)
1385                         return typeName;
1386                 for (int i = 0; i < from.Length; ++i)
1387                         typeName = typeName.Replace (from [i], to [i]);
1388                 return typeName;
1389         }
1390         
1391         // CREATE A STUB DOCUMENTATION FILE     
1392
1393         public static XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1394         {
1395                 string typesig = MakeTypeSignature(type);
1396                 if (typesig == null) return null; // not publicly visible
1397                 
1398                 XmlDocument doc = new XmlDocument();
1399                 XmlElement root = doc.CreateElement("Type");
1400                 doc.AppendChild (root);
1401
1402                 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1403                 
1404                 return root;
1405         }
1406
1407         private static XmlElement CreateSinceNode (XmlDocument doc)
1408         {
1409                 XmlElement s = doc.CreateElement ("since");
1410                 s.SetAttribute ("version", since);
1411                 return s;
1412         }
1413         
1414         // STUBBING/UPDATING FUNCTIONS
1415         
1416         public static void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1417         {
1418                 root.SetAttribute("Name", GetDocTypeName (type));
1419                 root.SetAttribute("FullName", GetDocTypeFullName (type));
1420
1421                 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1422                 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1423                 
1424                 XmlElement ass = WriteElement(root, "AssemblyInfo");
1425                 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1426                 if (!no_assembly_versions) {
1427                         UpdateAssemblyVersions (root, type, true);
1428                 }
1429                 else {
1430                         var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1431                         foreach (var version in versions)
1432                                 ass.RemoveChild (version);
1433                 }
1434                 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1435                         WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1436                 else
1437                         ClearElement(ass, "AssemblyCulture");
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                 if (type.IsGenericType ()) {
1450                         MakeTypeParameters (root, type.GenericParameters);
1451                 } else {
1452                         ClearElement(root, "TypeParameters");
1453                 }
1454                 
1455                 if (type.BaseType != null) {
1456                         XmlElement basenode = WriteElement(root, "Base");
1457                         
1458                         string basetypename = GetDocTypeFullName (type.BaseType);
1459                         if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1460                         WriteElementText(root, "Base/BaseTypeName", basetypename);
1461                         
1462                         // Document how this type instantiates the generic parameters of its base type
1463                         TypeReference origBase = type.BaseType.GetOriginalType ();
1464                         if (origBase.IsGenericType ()) {
1465                                 ClearElement(basenode, "BaseTypeArguments");
1466                                 GenericInstanceType baseInst             = type.BaseType as GenericInstanceType;
1467                                 GenericArgumentCollection baseGenArgs    = baseInst == null ? null : baseInst.GenericArguments;
1468                                 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1469                                 if (baseGenArgs.Count != baseGenParams.Count)
1470                                         throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1471                                 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1472                                         GenericParameter param = baseGenParams [i];
1473                                         TypeReference    value = baseGenArgs [i];
1474
1475                                         XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1476                                         XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1477                                         bta.AppendChild(arg);
1478                                         arg.SetAttribute ("TypeParamName", param.Name);
1479                                         arg.InnerText = GetDocTypeFullName (value);
1480                                 }
1481                         }
1482                 } else {
1483                         ClearElement(root, "Base");
1484                 }
1485
1486                 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1487                         IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1488                         List<string> interface_names = userInterfaces
1489                                         .Select (iface => GetDocTypeFullName (iface))
1490                                         .OrderBy (s => s)
1491                                         .ToList ();
1492
1493                         XmlElement interfaces = WriteElement(root, "Interfaces");
1494                         interfaces.RemoveAll();
1495                         foreach (string iname in interface_names) {
1496                                 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1497                                 interfaces.AppendChild(iface);
1498                                 WriteElementText(iface, "InterfaceName", iname);
1499                         }
1500                 } else {
1501                         ClearElement(root, "Interfaces");
1502                 }
1503
1504                 MakeAttributes (root, type.CustomAttributes, false);
1505                 
1506                 if (DocUtils.IsDelegate (type)) {
1507                         MakeTypeParameters (root, type.GenericParameters);
1508                         MakeParameters(root, type.GetMethod("Invoke").Parameters);
1509                         MakeReturnValue(root, type.GetMethod("Invoke"));
1510                 }
1511                 
1512                 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1513                 if (ecmaDocsType != null) {
1514                         if (ecmaDocsType.Name != "Docs") {
1515                                 int depth = ecmaDocsType.Depth;
1516                                 while (ecmaDocsType.Read ()) {
1517                                         if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1518                                                 break;
1519                                 }
1520                         }
1521                         if (!ecmaDocsType.IsStartElement ("Docs"))
1522                                 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1523                         typeInfo.EcmaDocs = ecmaDocsType;
1524                 }
1525                 MakeDocNode (typeInfo);
1526                 
1527                 if (!DocUtils.IsDelegate (type))
1528                         WriteElement (root, "Members");
1529
1530                 NormalizeWhitespace(root);
1531         }
1532
1533         static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1534         {
1535                 List<T> l = new List<T> (list);
1536                 l.Sort ();
1537                 return l;
1538         }
1539
1540         private static void UpdateMember (DocsNodeInfo info)
1541         {
1542                 XmlElement me = (XmlElement) info.Node;
1543                 IMemberReference mi = info.Member;
1544                 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1545                 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1546
1547                 WriteElementText(me, "MemberType", GetMemberType(mi));
1548                 
1549                 if (!no_assembly_versions) {
1550                         UpdateAssemblyVersions (me, mi, true);
1551                 }
1552                 else {
1553                         ClearElement (me, "AssemblyInfo");
1554                 }
1555                 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1556                 if (p != null)
1557                         MakeAttributes (me, p.CustomAttributes, false);
1558                 MakeReturnValue(me, mi);
1559                 if (mi is MethodReference) {
1560                         MethodReference mb = (MethodReference) mi;
1561                         if (mb.IsGenericMethod ())
1562                                 MakeTypeParameters (me, mb.GenericParameters);
1563                 }
1564                 MakeParameters(me, mi);
1565                 
1566                 string fieldValue;
1567                 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1568                         WriteElementText(me, "MemberValue", fieldValue);
1569                 
1570                 info.Node = WriteElement (me, "Docs");
1571                 MakeDocNode (info);
1572                 UpdateExtensionMethods (me, info);
1573         }
1574
1575         static readonly string[] ValidExtensionMembers = {
1576                 "Docs",
1577                 "MemberSignature",
1578                 "MemberType",
1579                 "Parameters",
1580                 "ReturnValue",
1581                 "TypeParameters",
1582         };
1583
1584         static readonly string[] ValidExtensionDocMembers = {
1585                 "param",
1586                 "summary",
1587                 "typeparam",
1588         };
1589
1590         private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1591         {
1592                 MethodDefinition me = info.Member as MethodDefinition;
1593                 if (me == null)
1594                         return;
1595                 if (info.Parameters.Count < 1)
1596                         return;
1597                 if (!DocUtils.IsExtensionMethod (me))
1598                         return;
1599
1600                 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1601                 XmlNode member = e.CloneNode (true);
1602                 em.AppendChild (member);
1603                 RemoveExcept (member, ValidExtensionMembers);
1604                 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1605                 WriteElementText (member, "MemberType", "ExtensionMethod");
1606                 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1607                 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1608                 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1609                 member.AppendChild (link);
1610                 AddTargets (em, info);
1611
1612                 extensionMethods.Add (em);
1613         }
1614
1615         private static void RemoveExcept (XmlNode node, string[] except)
1616         {
1617                 if (node == null)
1618                         return;
1619                 MyXmlNodeList remove = null;
1620                 foreach (XmlNode n in node.ChildNodes) {
1621                         if (Array.BinarySearch (except, n.Name) < 0) {
1622                                 if (remove == null)
1623                                         remove = new MyXmlNodeList ();
1624                                 remove.Add (n);
1625                         }
1626                 }
1627                 if (remove != null)
1628                         foreach (XmlNode n in remove)
1629                                 node.RemoveChild (n);
1630         }
1631
1632         private static void AddTargets (XmlNode member, DocsNodeInfo info)
1633         {
1634                 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1635                 member.PrependChild (targets);
1636                 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1637                         AppendElementAttributeText (targets, "Target", "Type",
1638                                 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1639                 }
1640                 else {
1641                         GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1642                         ConstraintCollection constraints = gp.Constraints;
1643                         if (constraints.Count == 0)
1644                                 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1645                         else
1646                                 foreach (TypeReference c in constraints)
1647                                         AppendElementAttributeText(targets, "Target", "Type",
1648                                                 slashdocFormatter.GetDeclaration (c));
1649                 }
1650         }
1651         
1652         private static bool GetFieldConstValue (FieldDefinition field, out string value)
1653         {
1654                 value = null;
1655                 TypeDefinition type = DocUtils.GetTypeDefinition (field.DeclaringType);
1656                 if (type != null && type.IsEnum) return false;
1657                 
1658                 if (type != null && type.IsGenericType ()) return false;
1659                 if (!field.HasConstant)
1660                         return false;
1661                 if (field.IsLiteral) {
1662                         object val = field.Constant;
1663                         if (val == null) value = "null";
1664                         else if (val is Enum) value = val.ToString();
1665                         else if (val is IFormattable) {
1666                                 value = ((IFormattable)val).ToString();
1667                                 if (val is string)
1668                                         value = "\"" + value + "\"";
1669                         }
1670                         if (value != null && value != "")
1671                                 return true;
1672                 }
1673                 return false;
1674         }
1675         
1676         // XML HELPER FUNCTIONS
1677         
1678         private static XmlElement WriteElement(XmlNode parent, string element) {
1679                 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1680                 if (ret == null) {
1681                         string[] path = element.Split('/');
1682                         foreach (string p in path) {
1683                                 ret = (XmlElement)parent.SelectSingleNode(p);
1684                                 if (ret == null) {
1685                                         string ename = p;
1686                                         if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1687                                                 ename = ename.Substring(0, ename.IndexOf('['));
1688                                         ret = parent.OwnerDocument.CreateElement(ename);
1689                                         parent.AppendChild(ret);
1690                                         parent = ret;
1691                                 } else {
1692                                         parent = ret;
1693                                 }
1694                         }
1695                 }
1696                 return ret;
1697         }
1698         private static void WriteElementText(XmlNode parent, string element, string value) {
1699                 XmlElement node = WriteElement(parent, element);
1700                 node.InnerText = value;
1701         }
1702
1703         static XmlElement AppendElementText (XmlNode parent, string element, string value)
1704         {
1705                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1706                 parent.AppendChild (n);
1707                 n.InnerText = value;
1708                 return n;
1709         }
1710
1711         static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1712         {
1713                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1714                 parent.AppendChild (n);
1715                 n.SetAttribute (attribute, value);
1716                 return n;
1717         }
1718
1719         private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1720         {
1721                 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1722                 dest.AppendChild (copy);
1723                 return copy;
1724         }
1725
1726         private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1727                 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1728                 if (node != null)
1729                         return;
1730                 node = WriteElement(parent, element);
1731                 node.InnerText = value;
1732         }
1733         private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1734                 XmlElement node = WriteElement(parent, element);
1735                 if (node.GetAttribute(attribute) == value) return;
1736                 node.SetAttribute(attribute, value);
1737         }
1738         private static void ClearElement(XmlElement parent, string name) {
1739                 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1740                 if (node != null)
1741                         parent.RemoveChild(node);
1742         }
1743         
1744         // DOCUMENTATION HELPER FUNCTIONS
1745         
1746         private static void MakeDocNode (DocsNodeInfo info)
1747         {
1748                 List<GenericParameter> genericParams      = info.GenericParameters;
1749                 ParameterDefinitionCollection parameters  = info.Parameters;
1750                 TypeReference returntype                  = info.ReturnType;
1751                 bool returnisreturn         = info.ReturnIsReturn;
1752                 XmlElement e                = info.Node;
1753                 bool addremarks             = info.AddRemarks;
1754
1755                 WriteElementInitialText(e, "summary", "To be added.");
1756                 
1757                 if (parameters != null) {
1758                         string[] values = new string [parameters.Count];
1759                         for (int i = 0; i < values.Length; ++i)
1760                                 values [i] = parameters [i].Name;
1761                         UpdateParameters (e, "param", values);
1762                 }
1763
1764                 if (genericParams != null) {
1765                         string[] values = new string [genericParams.Count];
1766                         for (int i = 0; i < values.Length; ++i)
1767                                 values [i] = genericParams [i].Name;
1768                         UpdateParameters (e, "typeparam", values);
1769                 }
1770
1771                 string retnodename = null;
1772                 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1773                         retnodename = returnisreturn ? "returns" : "value";
1774                         string retnodename_other = !returnisreturn ? "returns" : "value";
1775                         
1776                         // If it has a returns node instead of a value node, change its name.
1777                         XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1778                         if (retother != null) {
1779                                 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1780                                 foreach (XmlNode node in retother)
1781                                         retnode.AppendChild(node.CloneNode(true));
1782                                 e.ReplaceChild(retnode, retother);
1783                         } else {
1784                                 WriteElementInitialText(e, retnodename, "To be added.");
1785                         }
1786                 } else {
1787                         ClearElement(e, "returns");
1788                         ClearElement(e, "value");
1789                 }
1790
1791                 if (addremarks)
1792                         WriteElementInitialText(e, "remarks", "To be added.");
1793
1794                 if (exceptions.HasValue && info.Member != null) {
1795                         UpdateExceptions (e, info.Member);
1796                 }
1797
1798                 if (info.EcmaDocs != null) {
1799                         XmlReader r = info.EcmaDocs;
1800                         int depth = r.Depth;
1801                         r.ReadStartElement ("Docs");
1802                         while (r.Read ()) {
1803                                 if (r.Name == "Docs") {
1804                                         if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1805                                                 break;
1806                                         else
1807                                                 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1808                                 }
1809                                 if (!r.IsStartElement ())
1810                                         continue;
1811                                 switch (r.Name) {
1812                                         case "param":
1813                                         case "typeparam": {
1814                                                 XmlNode doc = e.SelectSingleNode (
1815                                                                 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1816                                                 string value = r.ReadInnerXml ();
1817                                                 if (doc != null)
1818                                                         doc.InnerXml = value.Replace ("\r", "");
1819                                                 break;
1820                                         }
1821                                         case "altmember":
1822                                         case "exception":
1823                                         case "permission":
1824                                         case "seealso": {
1825                                                 string name = r.Name;
1826                                                 string cref = r.GetAttribute ("cref");
1827                                                 XmlNode doc = e.SelectSingleNode (
1828                                                                 r.Name + "[@cref='" + cref + "']");
1829                                                 string value = r.ReadInnerXml ().Replace ("\r", "");
1830                                                 if (doc != null)
1831                                                         doc.InnerXml = value;
1832                                                 else {
1833                                                         XmlElement n = e.OwnerDocument.CreateElement (name);
1834                                                         n.SetAttribute ("cref", cref);
1835                                                         n.InnerXml = value;
1836                                                         e.AppendChild (n);
1837                                                 }
1838                                                 break;
1839                                         }
1840                                         default: {
1841                                                 string name = r.Name;
1842                                                 string xpath = r.Name;
1843                                                 StringList attributes = new StringList (r.AttributeCount);
1844                                                 if (r.MoveToFirstAttribute ()) {
1845                                                         do {
1846                                                                 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1847                                                         } while (r.MoveToNextAttribute ());
1848                                                         r.MoveToContent ();
1849                                                 }
1850                                                 if (attributes.Count > 0) {
1851                                                         xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1852                                                 }
1853                                                 XmlNode doc = e.SelectSingleNode (xpath);
1854                                                 string value = r.ReadInnerXml ().Replace ("\r", "");
1855                                                 if (doc != null) {
1856                                                         doc.InnerXml = value;
1857                                                 }
1858                                                 else {
1859                                                         XmlElement n = e.OwnerDocument.CreateElement (name);
1860                                                         n.InnerXml = value;
1861                                                         foreach (string a in attributes) {
1862                                                                 int eq = a.IndexOf ('=');
1863                                                                 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1864                                                         }
1865                                                         e.AppendChild (n);
1866                                                 }
1867                                                 break;
1868                                         }
1869                                 }
1870                         }
1871                 }
1872                 if (info.SlashDocs != null) {
1873                         XmlNode elem = info.SlashDocs;
1874                         if (elem != null) {
1875                                 if (elem.SelectSingleNode("summary") != null)
1876                                         ClearElement(e, "summary");
1877                                 if (elem.SelectSingleNode("remarks") != null)
1878                                         ClearElement(e, "remarks");
1879                                 if (elem.SelectSingleNode("value") != null)
1880                                         ClearElement(e, "value");
1881                                 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1882                                         ClearElement(e, retnodename);
1883
1884                                 foreach (XmlNode child in elem.ChildNodes) {
1885                                         switch (child.Name) {
1886                                                 case "param":
1887                                                 case "typeparam": {
1888                                                         XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
1889                                                         if (p2 != null)
1890                                                                 p2.InnerXml = child.InnerXml;
1891                                                         break;
1892                                                 }
1893                                                 case "altmember":
1894                                                 case "exception":
1895                                                 case "permission": {
1896                                                         XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
1897                                                         if (a == null) {
1898                                                                 a = e.OwnerDocument.CreateElement (child.Name);
1899                                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1900                                                                 e.AppendChild (a);
1901                                                         }
1902                                                         a.InnerXml = child.InnerXml;
1903                                                         break;
1904                                                 }
1905                                                 case "seealso": {
1906                                                         XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
1907                                                         if (a == null) {
1908                                                                 a = e.OwnerDocument.CreateElement ("altmember");
1909                                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1910                                                                 e.AppendChild (a);
1911                                                         }
1912                                                         break;
1913                                                 }
1914                                                 default:
1915                                                         CopyNode (child, e);
1916                                                         break;
1917                                         }
1918                                 }
1919                         }
1920                 }
1921                 
1922                 OrderDocsNodes (e, e.ChildNodes);
1923                 NormalizeWhitespace(e);
1924         }
1925
1926         static readonly string[] DocsNodeOrder = {
1927                 "typeparam", "param", "summary", "returns", "value", "remarks",
1928         };
1929
1930         private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1931         {
1932                 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1933                 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1934                         for (int j = 0; j < children.Count; ++j) {
1935                                 XmlNode c = children [j];
1936                                 if (c.Name == DocsNodeOrder [i]) {
1937                                         newChildren.Add (c);
1938                                 }
1939                         }
1940                 }
1941                 if (newChildren.Count >= 0)
1942                         docs.PrependChild ((XmlNode) newChildren [0]);
1943                 for (int i = 1; i < newChildren.Count; ++i) {
1944                         XmlNode prev = (XmlNode) newChildren [i-1];
1945                         XmlNode cur  = (XmlNode) newChildren [i];
1946                         docs.RemoveChild (cur);
1947                         docs.InsertAfter (cur, prev);
1948                 }
1949         }
1950         
1951
1952         private static void UpdateParameters (XmlElement e, string element, string[] values)
1953         {       
1954                 if (values != null) {
1955                         XmlNode[] paramnodes = new XmlNode[values.Length];
1956                         
1957                         // Some documentation had param nodes with leading spaces.
1958                         foreach (XmlElement paramnode in e.SelectNodes(element)){
1959                                 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1960                         }
1961                         
1962                         // If a member has only one parameter, we can track changes to
1963                         // the name of the parameter easily.
1964                         if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1965                                 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1966                         }
1967
1968                         bool reinsert = false;
1969
1970                         // Pick out existing and still-valid param nodes, and
1971                         // create nodes for parameters not in the file.
1972                         Hashtable seenParams = new Hashtable();
1973                         for (int pi = 0; pi < values.Length; pi++) {
1974                                 string p = values [pi];
1975                                 seenParams[p] = pi;
1976                                 
1977                                 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1978                                 if (paramnodes[pi] != null) continue;
1979                                 
1980                                 XmlElement pe = e.OwnerDocument.CreateElement(element);
1981                                 pe.SetAttribute("name", p);
1982                                 pe.InnerText = "To be added.";
1983                                 paramnodes[pi] = pe;
1984                                 reinsert = true;
1985                         }
1986
1987                         // Remove parameters that no longer exist and check all params are in the right order.
1988                         int idx = 0;
1989                         MyXmlNodeList todelete = new MyXmlNodeList ();
1990                         foreach (XmlElement paramnode in e.SelectNodes(element)) {
1991                                 string name = paramnode.GetAttribute("name");
1992                                 if (!seenParams.ContainsKey(name)) {
1993                                         if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1994                                                 Error ("The following param node can only be deleted if the --delete option is given: ");
1995                                                 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1996                                                         // delegate type
1997                                                         Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1998                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1999                                                                         name);
2000                                                 }
2001                                                 else {
2002                                                         Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2003                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2004                                                                         e.ParentNode.Attributes ["MemberName"].Value, 
2005                                                                         name);
2006                                                 }
2007                                                 Error ("\tValue={0}", paramnode.OuterXml);
2008                                         } else {
2009                                                 todelete.Add (paramnode);
2010                                         }
2011                                         continue;
2012                                 }
2013
2014                                 if ((int)seenParams[name] != idx)
2015                                         reinsert = true;
2016                                 
2017                                 idx++;
2018                         }
2019
2020                         foreach (XmlNode n in todelete) {
2021                                 n.ParentNode.RemoveChild (n);
2022                         }
2023                         
2024                         // Re-insert the parameter nodes at the top of the doc section.
2025                         if (reinsert)
2026                                 for (int pi = values.Length-1; pi >= 0; pi--)
2027                                         e.PrependChild(paramnodes[pi]);
2028                 } else {
2029                         // Clear all existing param nodes
2030                         foreach (XmlNode paramnode in e.SelectNodes(element)) {
2031                                 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2032                                         Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2033                                         Console.WriteLine(paramnode.OuterXml);
2034                                 } else {
2035                                         paramnode.ParentNode.RemoveChild(paramnode);
2036                                 }
2037                         }
2038                 }
2039         }
2040
2041         private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2042         {
2043                 string existingName = pe.GetAttribute ("name");
2044                 pe.SetAttribute ("name", newName);
2045                 if (existingName == newName)
2046                         return;
2047                 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2048                         if (paramref.GetAttribute ("name").Trim () == existingName)
2049                                 paramref.SetAttribute ("name", newName);
2050         }
2051
2052         class CrefComparer : XmlNodeComparer {
2053
2054                 public CrefComparer ()
2055                 {
2056                 }
2057
2058                 public override int Compare (XmlNode x, XmlNode y)
2059                 {
2060                         string xType = x.Attributes ["cref"].Value;
2061                         string yType = y.Attributes ["cref"].Value;
2062                         string xNamespace = GetNamespace (xType);
2063                         string yNamespace = GetNamespace (yType);
2064
2065                         int c = xNamespace.CompareTo (yNamespace);
2066                         if (c != 0)
2067                                 return c;
2068                         return xType.CompareTo (yType);
2069                 }
2070
2071                 static string GetNamespace (string type)
2072                 {
2073                         int n = type.LastIndexOf ('.');
2074                         if (n >= 0)
2075                                 return type.Substring (0, n);
2076                         return string.Empty;
2077                 }
2078         }
2079         
2080         private static void UpdateExceptions (XmlNode docs, IMemberReference member)
2081         {
2082                 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2083                         string cref = slashdocFormatter.GetDeclaration (source.Exception);
2084                         var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2085                         if (node != null)
2086                                 continue;
2087                         XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2088                         e.SetAttribute ("cref", cref);
2089                         e.InnerXml = "To be added; from: <see cref=\"" + 
2090                                 string.Join ("\" />, <see cref=\"", 
2091                                                 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2092                                                 .ToArray ()) +
2093                                 "\" />";
2094                         docs.AppendChild (e);
2095                 }
2096                 SortXmlNodes (docs, docs.SelectNodes ("exception"), 
2097                                 new CrefComparer ());
2098         }
2099
2100         private static void NormalizeWhitespace(XmlElement e) {
2101                 // Remove all text and whitespace nodes from the element so it
2102                 // is outputted with nice indentation and no blank lines.
2103                 ArrayList deleteNodes = new ArrayList();
2104                 foreach (XmlNode n in e)
2105                         if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2106                                 deleteNodes.Add(n);
2107                 foreach (XmlNode n in deleteNodes)
2108                                 n.ParentNode.RemoveChild(n);
2109         }
2110         
2111         private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2112         {
2113                 TypeDefinition type = member as TypeDefinition;
2114                 if (type == null)
2115                         type = member.DeclaringType as TypeDefinition;
2116                 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2117         }
2118         
2119         private static string GetAssemblyVersion (AssemblyDefinition assembly)
2120         {
2121                 return assembly.Name.Version.ToString();
2122         }
2123         
2124         private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2125         {
2126                 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2127                 if (e == null) {
2128                         e = root.OwnerDocument.CreateElement("AssemblyInfo");
2129                         root.AppendChild(e);
2130                 }
2131                 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2132                 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2133                         foreach (string sv in assemblyVersions)
2134                                 if (v.InnerText == sv)
2135                                         matches.Add (v);
2136                 }
2137                 // matches.Count > 0 && add: ignore -- already present
2138                 if (matches.Count > 0 && !add) {
2139                         foreach (XmlNode c in matches)
2140                                 e.RemoveChild (c);
2141                 }
2142                 else if (matches.Count == 0 && add) {
2143                         foreach (string sv in assemblyVersions) {
2144                                 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2145                                 c.InnerText = sv;
2146                                 e.AppendChild(c);
2147                         }
2148                 }
2149                 // matches.Count == 0 && !add: ignore -- already not present
2150
2151                 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2152                 SortXmlNodes (e, avs, new VersionComparer ());
2153
2154                 return avs.Count != 0;
2155         }
2156
2157         // FIXME: get TypeReferences instead of string comparison?
2158         private static string[] IgnorableAttributes = {
2159                 // Security related attributes
2160                 "System.Reflection.AssemblyKeyFileAttribute",
2161                 "System.Reflection.AssemblyDelaySignAttribute",
2162                 // Present in @RefType
2163                 "System.Runtime.InteropServices.OutAttribute",
2164                 // For naming the indexer to use when not using indexers
2165                 "System.Reflection.DefaultMemberAttribute",
2166                 // for decimal constants
2167                 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2168                 // compiler generated code
2169                 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2170                 // more compiler generated code, e.g. iterator methods
2171                 "System.Diagnostics.DebuggerHiddenAttribute",
2172                 "System.Runtime.CompilerServices.FixedBufferAttribute",
2173                 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2174                 // extension methods
2175                 "System.Runtime.CompilerServices.ExtensionAttribute",
2176         };
2177
2178         private static void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2179         {
2180                 if (attributes.Count == 0) {
2181                         ClearElement(root, "Attributes");
2182                         return;
2183                 }
2184
2185                 bool b = false;
2186                 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2187                 if (e != null)
2188                         e.RemoveAll();
2189                 else
2190                         e = root.OwnerDocument.CreateElement("Attributes");
2191                 
2192                 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2193                                 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2194                         if (!attribute.Resolve ()) {
2195                                 // skip?
2196                                 Error ("warning: could not resolve type {0}.",
2197                                                 attribute.Constructor.DeclaringType.FullName);
2198                         }
2199                         TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2200                         if (attrType != null && !IsPublic (attrType))
2201                                 continue;
2202                         if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2203                                 continue;
2204                         
2205                         if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2206                                 continue;
2207                         
2208                         b = true;
2209                         
2210                         StringList fields = new StringList ();
2211
2212                         ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2213                         for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2214                                 fields.Add (MakeAttributesValueString (
2215                                                 attribute.ConstructorParameters [i],
2216                                                 parameters [i].ParameterType));
2217                         }
2218                         var namedArgs =
2219                                 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2220                                  select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2221                                 .Concat (
2222                                                 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2223                                                  select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2224                                 .OrderBy (v => v.Name);
2225                         foreach (var d in namedArgs)
2226                                 fields.Add (string.Format ("{0}={1}", d.Name, 
2227                                                 MakeAttributesValueString (d.Value, d.Type)));
2228
2229                         string a2 = String.Join(", ", fields.ToArray ());
2230                         if (a2 != "") a2 = "(" + a2 + ")";
2231                         
2232                         XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2233                         e.AppendChild(ae);
2234                         
2235                         string name = attribute.Constructor.DeclaringType.FullName;
2236                         if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2237                         WriteElementText(ae, "AttributeName", name + a2);
2238                 }
2239                 
2240                 if (b && e.ParentNode == null)
2241                         root.AppendChild(e);
2242                 else if (!b)
2243                         ClearElement(root, "Attributes");
2244                 
2245                 NormalizeWhitespace(e);
2246         }
2247
2248         private static string MakeAttributesValueString (object v, TypeReference valueType)
2249         {
2250                 if (v == null)
2251                         return "null";
2252                 if (valueType.FullName == "System.Type")
2253                         return "typeof(" + v.ToString () + ")";
2254                 if (valueType.FullName == "System.String")
2255                         return "\"" + v.ToString () + "\"";
2256                 if (v is Boolean)
2257                         return (bool)v ? "true" : "false";
2258                 TypeDefinition valueDef = DocUtils.GetTypeDefinition (valueType);
2259                 if (valueDef == null || !valueDef.IsEnum)
2260                         return v.ToString ();
2261                 string typename = GetDocTypeFullName (valueType);
2262                 var values = GetEnumerationValues (valueDef);
2263                 ulong c = Convert.ToUInt64 (v);
2264                 if (values.ContainsKey (c))
2265                         return typename + "." + values [c];
2266                 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2267                                 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2268                         return string.Join (" | ",
2269                                         (from i in values.Keys
2270                                          where (c & i) != 0
2271                                          select typename + "." + values [i])
2272                                         .ToArray ());
2273                 }
2274                 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2275         }
2276
2277         private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2278         {
2279                 return
2280                         (from f in type.Fields.Cast<FieldDefinition> ()
2281                          where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2282                          select f)
2283                         .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2284         }
2285         
2286         private static void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2287         {
2288                 XmlElement e = WriteElement(root, "Parameters");
2289                 e.RemoveAll();
2290                 foreach (ParameterDefinition p in parameters) {
2291                         XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2292                         e.AppendChild(pe);
2293                         pe.SetAttribute("Name", p.Name);
2294                         pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2295                         if (p.ParameterType is ReferenceType) {
2296                                 if (p.IsOut) pe.SetAttribute("RefType", "out");
2297                                 else pe.SetAttribute("RefType", "ref");
2298                         }
2299                         MakeAttributes (pe, p.CustomAttributes, false);
2300                 }
2301         }
2302         
2303         private static void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2304         {
2305                 if (typeParams == null || typeParams.Count == 0) {
2306                         XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2307                         if (f != null)
2308                                 root.RemoveChild (f);
2309                         return;
2310                 }
2311                 XmlElement e = WriteElement(root, "TypeParameters");
2312                 e.RemoveAll();
2313                 foreach (GenericParameter t in typeParams) {
2314                         XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2315                         e.AppendChild(pe);
2316                         pe.SetAttribute("Name", t.Name);
2317                         MakeAttributes (pe, t.CustomAttributes, false);
2318                         XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2319                         ConstraintCollection constraints = t.Constraints;
2320                         GenericParameterAttributes attrs = t.Attributes;
2321                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2322                                 if (ce != null)
2323                                         e.RemoveChild (ce);
2324                                 continue;
2325                         }
2326                         if (ce != null)
2327                                 ce.RemoveAll();
2328                         else {
2329                                 ce = root.OwnerDocument.CreateElement ("Constraints");
2330                         }
2331                         pe.AppendChild (ce);
2332                         if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2333                                 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2334                         if ((attrs & GenericParameterAttributes.Covariant) != 0)
2335                                 AppendElementText (ce, "ParameterAttribute", "Covariant");
2336                         if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2337                                 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2338                         if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2339                                 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2340                         if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2341                                 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2342                         foreach (TypeReference c in constraints) {
2343                                 TypeDefinition cd = DocUtils.GetTypeDefinition (c);
2344                                 AppendElementText (ce,
2345                                                 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2346                                                 GetDocTypeFullName (c));
2347                         }
2348                 }
2349         }
2350
2351         private static void MakeParameters (XmlElement root, IMemberReference mi)
2352         {
2353                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2354                         MakeParameters (root, ((MethodDefinition)mi).Parameters);
2355                 else if (mi is MethodDefinition) {
2356                         MethodDefinition mb = (MethodDefinition) mi;
2357                         ParameterDefinitionCollection parameters = mb.Parameters;
2358                         MakeParameters(root, parameters);
2359                         if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2360                                 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2361                                 p.SetAttribute ("RefType", "this");
2362                         }
2363                 }
2364                 else if (mi is PropertyDefinition) {
2365                         ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2366                         if (parameters.Count > 0)
2367                                 MakeParameters(root, parameters);
2368                         else
2369                                 return;
2370                 }
2371                 else if (mi is FieldDefinition) return;
2372                 else if (mi is EventDefinition) return;
2373                 else throw new ArgumentException();
2374         }
2375
2376         private static string GetDocParameterType (TypeReference type)
2377         {
2378                 return GetDocTypeFullName (type).Replace ("@", "&");
2379         }
2380
2381         private static void MakeReturnValue(XmlElement root, TypeReference type, CustomAttributeCollection attributes) 
2382         {
2383                 XmlElement e = WriteElement(root, "ReturnValue");
2384                 e.RemoveAll();
2385                 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2386                 if (attributes != null)
2387                         MakeAttributes(e, attributes, false);
2388         }
2389         
2390         private static void MakeReturnValue (XmlElement root, IMemberReference mi)
2391         {
2392                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2393                         return;
2394                 else if (mi is MethodDefinition)
2395                         MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2396                 else if (mi is PropertyDefinition)
2397                         MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2398                 else if (mi is FieldDefinition)
2399                         MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2400                 else if (mi is EventDefinition)
2401                         MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2402                 else
2403                         throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2404         }
2405         
2406         private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2407                 IMemberReference mi = info.Member;
2408                 if (mi is TypeDefinition) return null;
2409
2410                 string sigs = MakeMemberSignature(mi);
2411                 if (sigs == null) return null; // not publicly visible
2412                 
2413                 // no documentation for property/event accessors.  Is there a better way of doing this?
2414                 if (mi.Name.StartsWith("get_")) return null;
2415                 if (mi.Name.StartsWith("set_")) return null;
2416                 if (mi.Name.StartsWith("add_")) return null;
2417                 if (mi.Name.StartsWith("remove_")) return null;
2418                 if (mi.Name.StartsWith("raise_")) return null;
2419                 
2420                 XmlElement me = doc.CreateElement("Member");
2421                 me.SetAttribute("MemberName", GetMemberName (mi));
2422                 
2423                 info.Node = me;
2424                 UpdateMember(info);
2425
2426                 if (since != null) {
2427                         XmlNode docs = me.SelectSingleNode("Docs");
2428                         docs.AppendChild (CreateSinceNode (doc));
2429                 }
2430                 
2431                 return me;
2432         }
2433
2434         private static string GetMemberName (IMemberReference mi)
2435         {
2436                 MethodDefinition mb = mi as MethodDefinition;
2437                 if (mb == null) {
2438                         PropertyDefinition pi = mi as PropertyDefinition;
2439                         if (pi == null)
2440                                 return mi.Name;
2441                         return DocUtils.GetPropertyName (pi);
2442                 }
2443                 StringBuilder sb = new StringBuilder (mi.Name.Length);
2444                 if (!DocUtils.IsExplicitlyImplemented (mb))
2445                         sb.Append (mi.Name);
2446                 else {
2447                         TypeReference iface;
2448                         MethodReference ifaceMethod;
2449                         DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2450                         sb.Append (GetDocTypeFullName (iface));
2451                         sb.Append ('.');
2452                         sb.Append (ifaceMethod.Name);
2453                 }
2454                 if (mb.IsGenericMethod ()) {
2455                         GenericParameterCollection typeParams = mb.GenericParameters;
2456                         if (typeParams.Count > 0) {
2457                                 sb.Append ("<");
2458                                 sb.Append (typeParams [0].Name);
2459                                 for (int i = 1; i < typeParams.Count; ++i)
2460                                         sb.Append (",").Append (typeParams [i].Name);
2461                                 sb.Append (">");
2462                         }
2463                 }
2464                 return sb.ToString ();
2465         }
2466
2467         private static int CountChars (string s, char c)
2468         {
2469                 int count = 0;
2470                 for (int i = 0; i < s.Length; ++i) {
2471                         if (s [i] == c)
2472                                 ++count;
2473                 }
2474                 return count;
2475         }
2476         
2477         /// SIGNATURE GENERATION FUNCTIONS
2478         
2479         static string MakeTypeSignature (TypeReference type)
2480         {
2481                 return csharpFormatter.GetDeclaration (type);
2482         }
2483
2484         static string MakeMemberSignature (IMemberReference mi)
2485         {
2486                 return csharpFullFormatter.GetDeclaration (mi);
2487         }
2488
2489         static string GetMemberType (IMemberReference mi)
2490         {
2491                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2492                         return "Constructor";
2493                 if (mi is MethodDefinition)
2494                         return "Method";
2495                 if (mi is PropertyDefinition)
2496                         return "Property";
2497                 if (mi is FieldDefinition)
2498                         return "Field";
2499                 if (mi is EventDefinition)
2500                         return "Event";
2501                 throw new ArgumentException();
2502         }
2503
2504         private static string GetDocTypeName (TypeReference type)
2505         {
2506                 return docTypeFormatter.GetName (type);
2507         }
2508
2509         private static string GetDocTypeFullName (TypeReference type)
2510         {
2511                 return DocTypeFullMemberFormatter.Default.GetName (type);
2512         }
2513
2514         class DocsNodeInfo {
2515                 public DocsNodeInfo (XmlElement node)
2516                 {
2517                         this.Node = node;
2518                 }
2519
2520                 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2521                         : this (node)
2522                 {
2523                         SetType (type);
2524                 }
2525
2526                 public DocsNodeInfo (XmlElement node, IMemberReference member)
2527                         : this (node)
2528                 {
2529                         SetMemberInfo (member);
2530                 }
2531
2532                 public void SetType (TypeDefinition type)
2533                 {
2534                         if (type == null)
2535                                 throw new ArgumentNullException ("type");
2536                         GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2537                         List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2538                         int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2539                         for (int i = 0; i < declTypes.Count - 1; ++i) {
2540                                 int remove = System.Math.Min (maxGenArgs, 
2541                                                 DocUtils.GetGenericArgumentCount (declTypes [i]));
2542                                 maxGenArgs -= remove;
2543                                 while (remove-- > 0)
2544                                         GenericParameters.RemoveAt (0);
2545                         }
2546                         if (DocUtils.IsDelegate (type)) {
2547                                 Parameters = type.GetMethod("Invoke").Parameters;
2548                                 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2549                         }
2550                         SetSlashDocs (type);
2551                 }
2552
2553                 public void SetMemberInfo (IMemberReference member)
2554                 {
2555                         if (member == null)
2556                                 throw new ArgumentNullException ("member");
2557                         ReturnIsReturn = true;
2558                         AddRemarks = true;
2559                         Member = member;
2560                         
2561                         if (member is MethodReference ) {
2562                                 MethodReference mr = (MethodReference) member;
2563                                 Parameters = mr.Parameters;
2564                                 if (mr.IsGenericMethod ()) {
2565                                         GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2566                                 }
2567                         }
2568                         else if (member is PropertyDefinition) {
2569                                 Parameters = ((PropertyDefinition) member).Parameters;
2570                         }
2571                                 
2572                         if (member is MethodDefinition) {
2573                                 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2574                         } else if (member is PropertyDefinition) {
2575                                 ReturnType = ((PropertyDefinition) member).PropertyType;
2576                                 ReturnIsReturn = false;
2577                         }
2578
2579                         // no remarks section for enum members
2580                         if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2581                                 AddRemarks = false;
2582                         SetSlashDocs (member);
2583                 }
2584
2585                 private void SetSlashDocs (IMemberReference member)
2586                 {
2587                         if (slashdocs == null)
2588                                 return;
2589
2590                         string slashdocsig = slashdocFormatter.GetDeclaration (member);
2591                         if (slashdocsig != null)
2592                                 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2593                 }
2594
2595                 public TypeReference ReturnType;
2596                 public List<GenericParameter> GenericParameters;
2597                 public ParameterDefinitionCollection Parameters;
2598                 public bool ReturnIsReturn;
2599                 public XmlElement Node;
2600                 public bool AddRemarks = true;
2601                 public XmlNode SlashDocs;
2602                 public XmlReader EcmaDocs;
2603                 public IMemberReference Member;
2604         }
2605
2606         static string GetXPathForMember (DocumentationMember member)
2607         {
2608                 StringBuilder xpath = new StringBuilder ();
2609                 xpath.Append ("//Members/Member[@MemberName=\"")
2610                         .Append (member.MemberName)
2611                         .Append ("\"]");
2612                 if (member.Parameters != null && member.Parameters.Count > 0) {
2613                         xpath.Append ("/Parameters[count(Parameter) = ")
2614                                 .Append (member.Parameters.Count);
2615                         for (int i = 0; i < member.Parameters.Count; ++i) {
2616                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2617                                 xpath.Append (member.Parameters [i]);
2618                                 xpath.Append ("\"");
2619                         }
2620                         xpath.Append ("]/..");
2621                 }
2622                 return xpath.ToString ();
2623         }
2624
2625         public static string GetXPathForMember (XPathNavigator member)
2626         {
2627                 StringBuilder xpath = new StringBuilder ();
2628                 xpath.Append ("//Type[@FullName=\"")
2629                         .Append (member.SelectSingleNode ("../../@FullName").Value)
2630                         .Append ("\"]/");
2631                 xpath.Append ("Members/Member[@MemberName=\"")
2632                         .Append (member.SelectSingleNode ("@MemberName").Value)
2633                         .Append ("\"]");
2634                 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2635                 if (parameters.Count > 0) {
2636                         xpath.Append ("/Parameters[count(Parameter) = ")
2637                                 .Append (parameters.Count);
2638                         int i = 0;
2639                         while (parameters.MoveNext ()) {
2640                                 ++i;
2641                                 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2642                                 xpath.Append (parameters.Current.Value);
2643                                 xpath.Append ("\"");
2644                         }
2645                         xpath.Append ("]/..");
2646                 }
2647                 return xpath.ToString ();
2648         }
2649
2650         public static string GetXPathForMember (IMemberReference member)
2651         {
2652                 StringBuilder xpath = new StringBuilder ();
2653                 xpath.Append ("//Type[@FullName=\"")
2654                         .Append (member.DeclaringType.FullName)
2655                         .Append ("\"]/");
2656                 xpath.Append ("Members/Member[@MemberName=\"")
2657                         .Append (GetMemberName (member))
2658                         .Append ("\"]");
2659
2660                 ParameterDefinitionCollection parameters = null;
2661                 if (member is MethodDefinition)
2662                         parameters = ((MethodDefinition) member).Parameters;
2663                 else if (member is PropertyDefinition) {
2664                         parameters = ((PropertyDefinition) member).Parameters;
2665                 }
2666                 if (parameters != null && parameters.Count > 0) {
2667                         xpath.Append ("/Parameters[count(Parameter) = ")
2668                                 .Append (parameters.Count);
2669                         for (int i = 0; i < parameters.Count; ++i) {
2670                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2671                                 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2672                                 xpath.Append ("\"");
2673                         }
2674                         xpath.Append ("]/..");
2675                 }
2676                 return xpath.ToString ();
2677         }
2678 }
2679
2680 static class CecilExtensions {
2681         public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2682         {
2683                 foreach (var c in type.Constructors)
2684                         yield return (IMemberReference) c;
2685                 foreach (var e in type.Events)
2686                         yield return (IMemberReference) e;
2687                 foreach (var f in type.Fields)
2688                         yield return (IMemberReference) f;
2689                 foreach (var m in type.Methods)
2690                         yield return (IMemberReference) m;
2691                 foreach (var t in type.NestedTypes)
2692                         yield return (IMemberReference) t;
2693                 foreach (var p in type.Properties)
2694                         yield return (IMemberReference) p;
2695         }
2696
2697         public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2698         {
2699                 return GetMembers (type).Where (m => m.Name == member);
2700         }
2701
2702         public static IMemberReference GetMember (this TypeDefinition type, string member)
2703         {
2704                 return GetMembers (type, member).EnsureZeroOrOne ();
2705         }
2706
2707         static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2708         {
2709                 if (source.Count () > 1)
2710                         throw new InvalidOperationException ("too many matches");
2711                 return source.FirstOrDefault ();
2712         }
2713
2714         static T EnsureOne<T> (this IEnumerable<T> source)
2715         {
2716                 if (source.Count () > 1)
2717                         throw new InvalidOperationException ("too many matches: " +
2718                                         string.Join ("; ", source.Select (e => e.ToString ()).ToArray ()));
2719                 return source.First ();
2720         }
2721
2722         public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2723         {
2724                 return type.Methods.Cast<MethodDefinition> ()
2725                         .Where (m => m.Name == method)
2726                         .EnsureZeroOrOne ();
2727         }
2728
2729         public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2730         {
2731                 TypeDefinition def = type as TypeDefinition;
2732                 if (def == null)
2733                         return new IMemberReference [0];
2734                 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2735                                 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2736                                 .FirstOrDefault ();
2737                 if (defMemberAttr == null)
2738                         return new IMemberReference [0];
2739                 string name = (string) defMemberAttr.ConstructorParameters [0];
2740                 return def.Properties.Cast<PropertyDefinition> ()
2741                                 .Where (p => p.Name == name)
2742                                 .Select (p => (IMemberReference) p);
2743         }
2744
2745         public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2746         {
2747                 return assembly.Modules.Cast<ModuleDefinition> ()
2748                                 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2749         }
2750
2751         public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2752         {
2753                 return GetTypes (assembly)
2754                         .Where (td => td.FullName == type)
2755                         .EnsureZeroOrOne ();
2756         }
2757
2758         public static bool IsGenericType (this TypeReference type)
2759         {
2760                 return type.GenericParameters.Count > 0;
2761         }
2762
2763         public static bool IsGenericMethod (this MethodReference method)
2764         {
2765                 return method.GenericParameters.Count > 0;
2766         }
2767
2768         public static IMemberReference GetDefinition (this IMemberReference member)
2769         {
2770                 EventReference er = member as EventReference;
2771                 if (er != null)
2772                         return GetEventDefinition (er);
2773                 FieldReference fr = member as FieldReference;
2774                 if (fr != null)
2775                         return GetFieldDefinition (fr);
2776                 MethodReference mr = member as MethodReference;
2777                 if (mr != null)
2778                         return GetMethodDefinition (mr);
2779                 PropertyReference pr = member as PropertyReference;
2780                 if (pr != null)
2781                         return GetPropertyDefinition (pr);
2782                 TypeReference tr = member as TypeReference;
2783                 if (tr != null)
2784                         return GetTypeDefinition (tr);
2785                 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2786         }
2787
2788         public static EventDefinition GetEventDefinition (this EventReference ev)
2789         {
2790                 EventDefinition evDef = ev as EventDefinition;
2791                 if (evDef != null)
2792                         return evDef;
2793                 return (EventDefinition) ev.DeclaringType.GetTypeDefinition ()
2794                         .GetMember (ev.Name);
2795         }
2796
2797         public static FieldDefinition GetFieldDefinition (this FieldReference field)
2798         {
2799                 FieldDefinition fieldDef = field as FieldDefinition;
2800                 if (fieldDef != null)
2801                         return fieldDef;
2802                 return (FieldDefinition) field.DeclaringType.GetTypeDefinition ()
2803                         .GetMember (field.Name);
2804         }
2805
2806         public static MethodDefinition GetMethodDefinition (this MethodReference method)
2807         {
2808                 MethodDefinition methodDef = method as MethodDefinition;
2809                 if (methodDef != null)
2810                         return methodDef;
2811                 method = method.GetOriginalMethod ();
2812                 return method.DeclaringType.GetTypeDefinition ()
2813                         .GetMembers (method.Name).OfType<MethodDefinition> ()
2814                         .Where (m => 
2815                                         AreSame (method.ReturnType.ReturnType, m.ReturnType.ReturnType) &&
2816                                         AreSame (method.Parameters, m.Parameters))
2817                         .EnsureOne ();
2818         }
2819
2820         static bool AreSame (ParameterDefinitionCollection a, ParameterDefinitionCollection b)
2821         {
2822                 if (a.Count != b.Count)
2823                         return false;
2824
2825                 if (a.Count == 0)
2826                         return true;
2827
2828                 for (int i = 0; i < a.Count; i++) {
2829                         if (!AreSame (a [i].ParameterType, b [i].ParameterType))
2830                                 return false;
2831                 }
2832
2833                 return true;
2834         }
2835
2836         static bool AreSame (TypeReference a, TypeReference b)
2837         {
2838                 while (a is TypeSpecification || b is TypeSpecification) {
2839                         if (a.GetType () != b.GetType ())
2840                                 return false;
2841
2842                         IGenericInstance ga = a as IGenericInstance;
2843                         IGenericInstance gb = b as IGenericInstance;
2844
2845                         a = ((TypeSpecification) a).ElementType;
2846                         b = ((TypeSpecification) b).ElementType;
2847
2848                         if (ga != null && gb != null) {
2849                                 if (ga.GenericArguments.Count != gb.GenericArguments.Count) {
2850                                         return false;
2851                                 }
2852                                 for (int i = 0; i < ga.GenericArguments.Count; ++i) {
2853                                         if (!AreSame (ga.GenericArguments [i], gb.GenericArguments [i]))
2854                                                 return false;
2855                                 }
2856                         }
2857                 }
2858
2859                 GenericParameter pa = (a as GenericParameter);
2860                 GenericParameter pb = (b as GenericParameter);
2861                 if ((pa != null) || (pb != null)) {
2862                         if (a.GetType () != b.GetType ())
2863                                 return false;
2864
2865                         return pa.Position == pb.Position;
2866                 }
2867
2868                 return a.FullName == b.FullName;
2869         }
2870
2871         public static PropertyDefinition GetPropertyDefinition (this PropertyReference property)
2872         {
2873                 PropertyDefinition propertyDef = property as PropertyDefinition;
2874                 if (propertyDef != null)
2875                         return propertyDef;
2876                 return (PropertyDefinition) property.DeclaringType.GetTypeDefinition ()
2877                         .GetMembers (property.Name).OfType<PropertyDefinition> ()
2878                         .Where (p => p.PropertyType.FullName == property.PropertyType.FullName &&
2879                                         AreSame (property.Parameters, p.Parameters))
2880                         .EnsureOne ();
2881         }
2882
2883         public static TypeDefinition GetTypeDefinition (this TypeReference type)
2884         {
2885                 return DocUtils.GetTypeDefinition (type);
2886         }
2887 }
2888
2889 static class DocUtils {
2890         public static bool IsExplicitlyImplemented (MethodDefinition method)
2891         {
2892                 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2893         }
2894
2895         public static string GetTypeDotMember (string name)
2896         {
2897                 int startType, startMethod;
2898                 startType = startMethod = -1;
2899                 for (int i = 0; i < name.Length; ++i) {
2900                         if (name [i] == '.') {
2901                                 startType = startMethod;
2902                                 startMethod = i;
2903                         }
2904                 }
2905                 return name.Substring (startType+1);
2906         }
2907
2908         public static string GetMember (string name)
2909         {
2910                 int i = name.LastIndexOf ('.');
2911                 if (i == -1)
2912                         return name;
2913                 return name.Substring (i+1);
2914         }
2915
2916         public static void GetInfoForExplicitlyImplementedMethod (
2917                         MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2918         {
2919                 iface = null;
2920                 ifaceMethod = null;
2921                 if (method.Overrides.Count != 1)
2922                         throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2923                 iface = method.Overrides [0].DeclaringType;
2924                 ifaceMethod = method.Overrides [0];
2925         }
2926
2927         public static string GetPropertyName (PropertyDefinition pi)
2928         {
2929                 // Issue: (g)mcs-generated assemblies that explicitly implement
2930                 // properties don't specify the full namespace, just the 
2931                 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2932                 MethodDefinition method = pi.GetMethod;
2933                 if (method == null)
2934                         method = pi.SetMethod;
2935                 if (!IsExplicitlyImplemented (method))
2936                         return pi.Name;
2937
2938                 // Need to determine appropriate namespace for this member.
2939                 TypeReference iface;
2940                 MethodReference ifaceMethod;
2941                 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2942                 return string.Join (".", new string[]{
2943                                 DocTypeFullMemberFormatter.Default.GetName (iface),
2944                                 GetMember (pi.Name)});
2945         }
2946
2947         public static string GetNamespace (TypeReference type)
2948         {
2949                 if (type.GetOriginalType ().IsNested)
2950                         type = type.GetOriginalType ();
2951                 while (type != null && type.IsNested)
2952                         type = type.DeclaringType;
2953                 if (type == null)
2954                         return string.Empty;
2955                 return type.Namespace;
2956         }
2957
2958         public static string PathCombine (string dir, string path)
2959         {
2960                 if (dir == null)
2961                         dir = "";
2962                 if (path == null)
2963                         path = "";
2964                 return Path.Combine (dir, path);
2965         }
2966
2967         public static bool IsExtensionMethod (MethodDefinition method)
2968         {
2969                 return
2970                         method.CustomAttributes.Cast<CustomAttribute> ()
2971                                         .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2972                                         .Any () &&
2973                         method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2974                                         .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2975                                         .Any ();
2976         }
2977
2978         public static bool IsDelegate (TypeDefinition type)
2979         {
2980                 TypeReference baseRef = type.BaseType;
2981                 if (baseRef == null)
2982                         return false;
2983                 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2984                                 baseRef.FullName == "System.MulticastDelegate";
2985         }
2986
2987         public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2988         {
2989                 List<TypeReference> decls = new List<TypeReference> ();
2990                 decls.Add (type);
2991                 while (type.DeclaringType != null) {
2992                         decls.Add (type.DeclaringType);
2993                         type = type.DeclaringType;
2994                 }
2995                 decls.Reverse ();
2996                 return decls;
2997         }
2998
2999         public static int GetGenericArgumentCount (TypeReference type)
3000         {
3001                 GenericInstanceType inst = type as GenericInstanceType;
3002                 return inst != null
3003                                 ? inst.GenericArguments.Count
3004                                 : type.GenericParameters.Count;
3005         }
3006
3007         public static TypeDefinition GetTypeDefinition (TypeReference type)
3008         {
3009                 // Remove generic instantiation info (so string comparison below works)
3010                 type = type.GetOriginalType ();
3011                 TypeDefinition typeDef = type as TypeDefinition;
3012                 if (typeDef != null)
3013                         return typeDef;
3014
3015                 AssemblyNameReference reference = type.Scope as AssemblyNameReference;
3016                 if (reference != null) {
3017                         AssemblyDefinition ad = type.Module.Assembly.Resolver.Resolve (reference);
3018                         if (ad != null && (typeDef = ad.MainModule.Types [type.FullName]) != null)
3019                                 return typeDef;
3020                 }
3021                 ModuleDefinition module = type.Scope as ModuleDefinition;
3022                 if (module != null && (typeDef = module.Types [type.FullName]) != null)
3023                         return typeDef;
3024                 return null;
3025         }
3026
3027         public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3028         {
3029                 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3030                 List<TypeReference> userInterfaces = new List<TypeReference> ();
3031                 foreach (TypeReference iface in type.Interfaces) {
3032                         TypeReference lookup = GetTypeDefinition (iface) ?? iface;
3033                         if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3034                                 userInterfaces.Add (iface);
3035                 }
3036                 return userInterfaces;
3037         }
3038
3039         private static string GetQualifiedTypeName (TypeReference type)
3040         {
3041                 return "[" + type.Scope.Name + "]" + type.FullName;
3042         }
3043
3044         private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3045         {
3046                 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3047                 Action<TypeDefinition> a = null;
3048                 a = t => {
3049                         if (t == null) return;
3050                         foreach (TypeReference r in t.Interfaces) {
3051                                 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3052                                 a (GetTypeDefinition (r));
3053                         }
3054                 };
3055                 TypeReference baseRef = type.BaseType;
3056                 while (baseRef != null) {
3057                         TypeDefinition baseDef = GetTypeDefinition (baseRef);
3058                         if (baseDef != null) {
3059                                 a (baseDef);
3060                                 baseRef = baseDef.BaseType;
3061                         }
3062                         else
3063                                 baseRef = null;
3064                 }
3065                 foreach (TypeReference r in type.Interfaces)
3066                         a (GetTypeDefinition (r));
3067                 return inheritedInterfaces;
3068         }
3069 }
3070
3071 class DocumentationMember {
3072         public StringToStringMap MemberSignatures = new StringToStringMap ();
3073         public string ReturnType;
3074         public StringList Parameters;
3075         public string MemberName;
3076         public string MemberType;
3077
3078         public DocumentationMember (XmlReader reader)
3079         {
3080                 MemberName = reader.GetAttribute ("MemberName");
3081                 int depth = reader.Depth;
3082                 bool go = true;
3083                 StringList p = new StringList ();
3084                 do {
3085                         if (reader.NodeType != XmlNodeType.Element)
3086                                 continue;
3087                         switch (reader.Name) {
3088                                 case "MemberSignature":
3089                                         MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3090                                         break;
3091                                 case "MemberType":
3092                                         MemberType = reader.ReadElementString ();
3093                                         break;
3094                                 case "ReturnType":
3095                                         if (reader.Depth == depth + 2)
3096                                                 ReturnType = reader.ReadElementString ();
3097                                         break;
3098                                 case "Parameter":
3099                                         if (reader.Depth == depth + 2)
3100                                                 p.Add (reader.GetAttribute ("Type"));
3101                                         break;
3102                                 case "Docs":
3103                                         if (reader.Depth == depth + 1)
3104                                                 go = false;
3105                                         break;
3106                         }
3107                 } while (go && reader.Read () && reader.Depth >= depth);
3108                 if (p.Count > 0) {
3109                         Parameters = p;
3110                 }
3111         }
3112
3113         public DocumentationMember (XmlNode node)
3114         {
3115                 MemberName = node.Attributes ["MemberName"].Value;
3116                 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3117                         XmlAttribute l = n.Attributes ["Language"];
3118                         XmlAttribute v = n.Attributes ["Value"];
3119                         if (l != null && v != null)
3120                                 MemberSignatures [l.Value] = v.Value;
3121                 }
3122                 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3123                 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3124                 if (rt != null)
3125                         ReturnType = rt.InnerText;
3126                 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3127                 if (p.Count > 0) {
3128                         Parameters = new StringList (p.Count);
3129                         for (int i = 0; i < p.Count; ++i)
3130                                 Parameters.Add (p [i].Attributes ["Type"].Value);
3131                 }
3132         }
3133 }
3134
3135 public abstract class MemberFormatter {
3136         public virtual string GetName (IMemberReference member)
3137         {
3138                 TypeReference type = member as TypeReference;
3139                 if (type != null)
3140                         return GetTypeName (type);
3141                 MethodReference method  = member as MethodReference;
3142                 if (method != null && method.Name == ".ctor") // method.IsConstructor
3143                         return GetConstructorName (method);
3144                 if (method != null)
3145                         return GetMethodName (method);
3146                 PropertyReference prop = member as PropertyReference;
3147                 if (prop != null)
3148                         return GetPropertyName (prop);
3149                 FieldReference field = member as FieldReference;
3150                 if (field != null)
3151                         return GetFieldName (field);
3152                 EventReference e = member as EventReference;
3153                 if (e != null)
3154                         return GetEventName (e);
3155                 throw new NotSupportedException ("Can't handle: " +
3156                                         (member == null ? "null" : member.GetType().ToString()));
3157         }
3158
3159         protected virtual string GetTypeName (TypeReference type)
3160         {
3161                 if (type == null)
3162                         throw new ArgumentNullException ("type");
3163                 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3164         }
3165
3166         protected virtual char[] ArrayDelimeters {
3167                 get {return new char[]{'[', ']'};}
3168         }
3169
3170         protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3171         {
3172                 if (type is ArrayType) {
3173                         TypeSpecification spec = type as TypeSpecification;
3174                         _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3175                                         .Append (ArrayDelimeters [0]);
3176                         ArrayType array = (ArrayType) type;
3177                         int rank = array.Rank;
3178                         if (rank > 1)
3179                                 buf.Append (new string (',', rank-1));
3180                         return buf.Append (ArrayDelimeters [1]);
3181                 }
3182                 if (type is ReferenceType) {
3183                         return AppendRefTypeName (buf, type);
3184                 }
3185                 if (type is PointerType) {
3186                         return AppendPointerTypeName (buf, type);
3187                 }
3188                 AppendNamespace (buf, type);
3189                 if (type is GenericParameter) {
3190                         return AppendTypeName (buf, type);
3191                 }
3192                 GenericInstanceType genInst = type as GenericInstanceType;
3193                 if (type.GenericParameters.Count == 0 &&
3194                                 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3195                         return AppendFullTypeName (buf, type);
3196                 }
3197                 return AppendGenericType (buf, type);
3198         }
3199
3200         protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3201         {
3202                 string ns = DocUtils.GetNamespace (type);
3203                 if (ns != null && ns.Length > 0)
3204                         buf.Append (ns).Append ('.');
3205                 return buf;
3206         }
3207
3208         private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3209         {
3210                 if (type.DeclaringType != null)
3211                         AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3212                 return AppendTypeName (buf, type);
3213         }
3214
3215         protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3216         {
3217                 return AppendTypeName (buf, type.Name);
3218         }
3219
3220         protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3221         {
3222                 int n = typename.IndexOf ("`");
3223                 if (n >= 0)
3224                         return buf.Append (typename.Substring (0, n));
3225                 return buf.Append (typename);
3226         }
3227
3228         protected virtual string RefTypeModifier {
3229                 get {return "@";}
3230         }
3231
3232         protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3233         {
3234                 TypeSpecification spec = type as TypeSpecification;
3235                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3236                                 .Append (RefTypeModifier);
3237         }
3238
3239         protected virtual string PointerModifier {
3240                 get {return "*";}
3241         }
3242
3243         protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3244         {
3245                 TypeSpecification spec = type as TypeSpecification;
3246                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3247                                 .Append (PointerModifier);
3248         }
3249
3250         protected virtual char[] GenericTypeContainer {
3251                 get {return new char[]{'<', '>'};}
3252         }
3253
3254         protected virtual char NestedTypeSeparator {
3255                 get {return '.';}
3256         }
3257
3258         protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3259         {
3260                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3261                                 type is GenericInstanceType ? type.GetOriginalType () : type);
3262                 List<TypeReference> genArgs = GetGenericArguments (type);
3263                 int argIdx = 0;
3264                 int prev = 0;
3265                 bool insertNested = false;
3266                 foreach (var decl in decls) {
3267                         TypeReference declDef = DocUtils.GetTypeDefinition (decl) ?? decl;
3268                         if (insertNested) {
3269                                 buf.Append (NestedTypeSeparator);
3270                         }
3271                         insertNested = true;
3272                         AppendTypeName (buf, declDef);
3273                         int ac = DocUtils.GetGenericArgumentCount (declDef);
3274                         int c = ac - prev;
3275                         prev = ac;
3276                         if (c > 0) {
3277                                 buf.Append (GenericTypeContainer [0]);
3278                                 _AppendTypeName (buf, genArgs [argIdx++]);
3279                                 for (int i = 1; i < c; ++i)
3280                                         _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3281                                 buf.Append (GenericTypeContainer [1]);
3282                         }
3283                 }
3284                 return buf;
3285         }
3286
3287         private List<TypeReference> GetGenericArguments (TypeReference type)
3288         {
3289                 var args = new List<TypeReference> ();
3290                 GenericInstanceType inst = type as GenericInstanceType;
3291                 if (inst != null)
3292                         args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3293                 else
3294                         args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3295                 return args;
3296         }
3297
3298         protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3299         {
3300                 return buf;
3301         }
3302
3303         protected virtual string GetConstructorName (MethodReference constructor)
3304         {
3305                 return constructor.Name;
3306         }
3307
3308         protected virtual string GetMethodName (MethodReference method)
3309         {
3310                 return method.Name;
3311         }
3312
3313         protected virtual string GetPropertyName (PropertyReference property)
3314         {
3315                 return property.Name;
3316         }
3317
3318         protected virtual string GetFieldName (FieldReference field)
3319         {
3320                 return field.Name;
3321         }
3322
3323         protected virtual string GetEventName (EventReference e)
3324         {
3325                 return e.Name;
3326         }
3327
3328         public virtual string GetDeclaration (IMemberReference member)
3329         {
3330                 if (member == null)
3331                         throw new ArgumentNullException ("member");
3332                 TypeDefinition type = member as TypeDefinition;
3333                 if (type != null)
3334                         return GetTypeDeclaration (type);
3335                 MethodDefinition method = member as MethodDefinition;
3336                 if (method != null && method.IsConstructor)
3337                         return GetConstructorDeclaration (method);
3338                 if (method != null)
3339                         return GetMethodDeclaration (method);
3340                 PropertyDefinition prop = member as PropertyDefinition;
3341                 if (prop != null)
3342                         return GetPropertyDeclaration (prop);
3343                 FieldDefinition field = member as FieldDefinition;
3344                 if (field != null)
3345                         return GetFieldDeclaration (field);
3346                 EventDefinition e = member as EventDefinition;
3347                 if (e != null)
3348                         return GetEventDeclaration (e);
3349                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3350         }
3351
3352         protected virtual string GetTypeDeclaration (TypeDefinition type)
3353         {
3354                 if (type == null)
3355                         throw new ArgumentNullException ("type");
3356                 StringBuilder buf = new StringBuilder (type.Name.Length);
3357                 _AppendTypeName (buf, type);
3358                 AppendGenericTypeConstraints (buf, type);
3359                 return buf.ToString ();
3360         }
3361
3362         protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3363         {
3364                 return GetConstructorName (constructor);
3365         }
3366
3367         protected virtual string GetMethodDeclaration (MethodDefinition method)
3368         {
3369                 // Special signature for destructors.
3370                 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3371                         return GetFinalizerName (method);
3372
3373                 StringBuilder buf = new StringBuilder ();
3374
3375                 AppendVisibility (buf, method);
3376                 if (buf.Length == 0 && 
3377                                 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3378                         return null;
3379
3380                 AppendModifiers (buf, method);
3381
3382                 if (buf.Length != 0)
3383                         buf.Append (" ");
3384                 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3385
3386                 AppendMethodName (buf, method);
3387                 AppendGenericMethod (buf, method).Append (" ");
3388                 AppendParameters (buf, method, method.Parameters);
3389                 AppendGenericMethodConstraints (buf, method);
3390                 return buf.ToString ();
3391         }
3392
3393         protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3394         {
3395                 return buf.Append (method.Name);
3396         }
3397
3398         protected virtual string GetFinalizerName (MethodDefinition method)
3399         {
3400                 return "Finalize";
3401         }
3402
3403         protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3404         {
3405                 return buf;
3406         }
3407
3408         protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3409         {
3410                 return buf;
3411         }
3412
3413         protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3414         {
3415                 return buf;
3416         }
3417
3418         protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3419         {
3420                 return buf;
3421         }
3422
3423         protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3424         {
3425                 return buf;
3426         }
3427
3428         protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3429         {
3430                 return GetPropertyName (property);
3431         }
3432
3433         protected virtual string GetFieldDeclaration (FieldDefinition field)
3434         {
3435                 return GetFieldName (field);
3436         }
3437
3438         protected virtual string GetEventDeclaration (EventDefinition e)
3439         {
3440                 return GetEventName (e);
3441         }
3442 }
3443
3444 class CSharpFullMemberFormatter : MemberFormatter {
3445
3446         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3447         {
3448                 string ns = DocUtils.GetNamespace (type);
3449                 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3450                         buf.Append (ns).Append ('.');
3451                 return buf;
3452         }
3453
3454         private string GetCSharpType (string t)
3455         {
3456                 switch (t) {
3457                 case "System.Byte":    return "byte";
3458                 case "System.SByte":   return "sbyte";
3459                 case "System.Int16":   return "short";
3460                 case "System.Int32":   return "int";
3461                 case "System.Int64":   return "long";
3462
3463                 case "System.UInt16":  return "ushort";
3464                 case "System.UInt32":  return "uint";
3465                 case "System.UInt64":  return "ulong";
3466
3467                 case "System.Single":  return "float";
3468                 case "System.Double":  return "double";
3469                 case "System.Decimal": return "decimal";
3470                 case "System.Boolean": return "bool";
3471                 case "System.Char":    return "char";
3472                 case "System.Void":    return "void";
3473                 case "System.String":  return "string";
3474                 case "System.Object":  return "object";
3475                 }
3476                 return null;
3477         }
3478
3479         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3480         {
3481                 if (type is GenericParameter)
3482                         return buf.Append (type.Name);
3483                 string t = type.FullName;
3484                 if (!t.StartsWith ("System.")) {
3485                         return base.AppendTypeName (buf, type);
3486                 }
3487
3488                 string s = GetCSharpType (t);
3489                 if (s != null)
3490                         return buf.Append (s);
3491                 
3492                 return base.AppendTypeName (buf, type);
3493         }
3494
3495         protected override string GetTypeDeclaration (TypeDefinition type)
3496         {
3497                 string visibility = GetTypeVisibility (type.Attributes);
3498                 if (visibility == null)
3499                         return null;
3500
3501                 StringBuilder buf = new StringBuilder ();
3502                 
3503                 buf.Append (visibility);
3504                 buf.Append (" ");
3505
3506                 MemberFormatter full = new CSharpFullMemberFormatter ();
3507
3508                 if (DocUtils.IsDelegate (type)) {
3509                         buf.Append("delegate ");
3510                         MethodDefinition invoke = type.GetMethod ("Invoke");
3511                         buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3512                         buf.Append (GetName (type));
3513                         AppendParameters (buf, invoke, invoke.Parameters);
3514                         AppendGenericTypeConstraints (buf, type);
3515                         buf.Append (";");
3516
3517                         return buf.ToString();
3518                 }
3519                 
3520                 if (type.IsAbstract && !type.IsInterface)
3521                         buf.Append("abstract ");
3522                 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3523                         buf.Append("sealed ");
3524                 buf.Replace ("abstract sealed", "static");
3525
3526                 buf.Append (GetTypeKind (type));
3527                 buf.Append (" ");
3528                 buf.Append (GetCSharpType (type.FullName) == null 
3529                                 ? GetName (type) 
3530                                 : type.Name);
3531
3532                 if (!type.IsEnum) {
3533                         TypeReference basetype = type.BaseType;
3534                         if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType)       // FIXME
3535                                 basetype = null;
3536
3537                         List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3538                                         .Select (iface => full.GetName (iface))
3539                                         .OrderBy (s => s)
3540                                         .ToList ();
3541
3542                         if (basetype != null || interface_names.Count > 0)
3543                                 buf.Append (" : ");
3544                         
3545                         if (basetype != null) {
3546                                 buf.Append (full.GetName (basetype));
3547                                 if (interface_names.Count > 0)
3548                                         buf.Append (", ");
3549                         }
3550                         
3551                         for (int i = 0; i < interface_names.Count; i++){
3552                                 if (i != 0)
3553                                         buf.Append (", ");
3554                                 buf.Append (interface_names [i]);
3555                         }
3556                         AppendGenericTypeConstraints (buf, type);
3557                 }
3558
3559                 return buf.ToString ();
3560         }
3561
3562         static string GetTypeKind (TypeDefinition t)
3563         {
3564                 if (t.IsEnum)
3565                         return "enum";
3566                 if (t.IsValueType)
3567                         return "struct";
3568                 if (t.IsClass || t.FullName == "System.Enum")
3569                         return "class";
3570                 if (t.IsInterface)
3571                         return "interface";
3572                 throw new ArgumentException(t.FullName);
3573         }
3574
3575         static string GetTypeVisibility (TypeAttributes ta)
3576         {
3577                 switch (ta & TypeAttributes.VisibilityMask) {
3578                 case TypeAttributes.Public:
3579                 case TypeAttributes.NestedPublic:
3580                         return "public";
3581
3582                 case TypeAttributes.NestedFamily:
3583                 case TypeAttributes.NestedFamORAssem:
3584                         return "protected";
3585
3586                 default:
3587                         return null;
3588                 }
3589         }
3590
3591         protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3592         {
3593                 if (type.GenericParameters.Count == 0)
3594                         return buf;
3595                 return AppendConstraints (buf, type.GenericParameters);
3596         }
3597
3598         private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3599         {
3600                 foreach (GenericParameter genArg in genArgs) {
3601                         GenericParameterAttributes attrs = genArg.Attributes;
3602                         ConstraintCollection constraints = genArg.Constraints;
3603                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3604                                 continue;
3605                         buf.Append (" where ").Append (genArg.Name).Append (" : ");
3606                         bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3607                         bool isvt  = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3608                         bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3609                         bool comma = false;
3610                         if (isref) {
3611                                 buf.Append ("class");
3612                                 comma = true;
3613                         }
3614                         else if (isvt) {
3615                                 buf.Append ("struct");
3616                                 comma = true;
3617                         }
3618                         if (constraints.Count > 0 && !isvt) {
3619                                 if (comma)
3620                                         buf.Append (", ");
3621                                 buf.Append (GetTypeName (constraints [0]));
3622                                 for (int i = 1; i < constraints.Count; ++i)
3623                                         buf.Append (", ").Append (GetTypeName (constraints [i]));
3624                         }
3625                         if (isnew && !isvt) {
3626                                 if (comma)
3627                                         buf.Append (", ");
3628                                 buf.Append ("new()");
3629                         }
3630                 }
3631                 return buf;
3632         }
3633
3634         protected override string GetConstructorDeclaration (MethodDefinition constructor)
3635         {
3636                 StringBuilder buf = new StringBuilder ();
3637                 AppendVisibility (buf, constructor);
3638                 if (buf.Length == 0)
3639                         return null;
3640
3641                 buf.Append (' ');
3642                 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3643                 AppendParameters (buf, constructor, constructor.Parameters);
3644                 buf.Append (';');
3645
3646                 return buf.ToString ();
3647         }
3648         
3649         protected override string GetMethodDeclaration (MethodDefinition method)
3650         {
3651                 string decl = base.GetMethodDeclaration (method);
3652                 if (decl != null)
3653                         return decl + ";";
3654                 return null;
3655         }
3656
3657         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3658         {
3659                 if (DocUtils.IsExplicitlyImplemented (method)) {
3660                         TypeReference iface;
3661                         MethodReference ifaceMethod;
3662                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3663                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3664                                 .Append ('.')
3665                                 .Append (ifaceMethod.Name);
3666                 }
3667                 return base.AppendMethodName (buf, method);
3668         }
3669
3670         protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3671         {
3672                 if (method.GenericParameters.Count == 0)
3673                         return buf;
3674                 return AppendConstraints (buf, method.GenericParameters);
3675         }
3676
3677         protected override string RefTypeModifier {
3678                 get {return "";}
3679         }
3680
3681         protected override string GetFinalizerName (MethodDefinition method)
3682         {
3683                 return "~" + method.DeclaringType.Name + " ()"; 
3684         }
3685
3686         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3687         {
3688                 if (method == null)
3689                         return buf;
3690                 if (method.IsPublic)
3691                         return buf.Append ("public");
3692                 if (method.IsFamily || method.IsFamilyOrAssembly)
3693                         return buf.Append ("protected");
3694                 return buf;
3695         }
3696
3697         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3698         {
3699                 string modifiers = String.Empty;
3700                 if (method.IsStatic) modifiers += " static";
3701                 if (method.IsVirtual && !method.IsAbstract) {
3702                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3703                         else modifiers += " override";
3704                 }
3705                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3706                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3707                 if (method.IsFinal) modifiers += " sealed";
3708                 if (modifiers == " virtual sealed") modifiers = "";
3709
3710                 return buf.Append (modifiers);
3711         }
3712
3713         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3714         {
3715                 if (method.IsGenericMethod ()) {
3716                         GenericParameterCollection args = method.GenericParameters;
3717                         if (args.Count > 0) {
3718                                 buf.Append ("<");
3719                                 buf.Append (args [0].Name);
3720                                 for (int i = 1; i < args.Count; ++i)
3721                                         buf.Append (",").Append (args [i].Name);
3722                                 buf.Append (">");
3723                         }
3724                 }
3725                 return buf;
3726         }
3727
3728         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3729         {
3730                 return AppendParameters (buf, method, parameters, '(', ')');
3731         }
3732
3733         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3734         {
3735                 buf.Append (begin);
3736
3737                 if (parameters.Count > 0) {
3738                         if (DocUtils.IsExtensionMethod (method))
3739                                 buf.Append ("this ");
3740                         AppendParameter (buf, parameters [0]);
3741                         for (int i = 1; i < parameters.Count; ++i) {
3742                                 buf.Append (", ");
3743                                 AppendParameter (buf, parameters [i]);
3744                         }
3745                 }
3746
3747                 return buf.Append (end);
3748         }
3749
3750         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3751         {
3752                 if (parameter.ParameterType is ReferenceType) {
3753                         if (parameter.IsOut)
3754                                 buf.Append ("out ");
3755                         else
3756                                 buf.Append ("ref ");
3757                 }
3758                 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3759                 return buf.Append (parameter.Name);
3760         }
3761
3762         protected override string GetPropertyDeclaration (PropertyDefinition property)
3763         {
3764                 MethodDefinition method;
3765
3766                 string get_visible = null;
3767                 if ((method = property.GetMethod) != null && 
3768                                 (DocUtils.IsExplicitlyImplemented (method) || 
3769                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3770                         get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3771                 string set_visible = null;
3772                 if ((method = property.SetMethod) != null &&
3773                                 (DocUtils.IsExplicitlyImplemented (method) || 
3774                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3775                         set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3776
3777                 if ((set_visible == null) && (get_visible == null))
3778                         return null;
3779
3780                 string visibility;
3781                 StringBuilder buf = new StringBuilder ();
3782                 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3783                         buf.Append (visibility = get_visible);
3784                 else if (set_visible != null && get_visible == null)
3785                         buf.Append (visibility = set_visible);
3786                 else
3787                         buf.Append (visibility = "public");
3788
3789                 // Pick an accessor to use for static/virtual/override/etc. checks.
3790                 method = property.SetMethod;
3791                 if (method == null)
3792                         method = property.GetMethod;
3793         
3794                 string modifiers = String.Empty;
3795                 if (method.IsStatic) modifiers += " static";
3796                 if (method.IsVirtual && !method.IsAbstract) {
3797                                 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3798                                         modifiers += " virtual";
3799                                 else
3800                                         modifiers += " override";
3801                 }
3802                 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3803                 if (method.IsAbstract && !declDef.IsInterface)
3804                         modifiers += " abstract";
3805                 if (method.IsFinal)
3806                         modifiers += " sealed";
3807                 if (modifiers == " virtual sealed")
3808                         modifiers = "";
3809                 buf.Append (modifiers).Append (' ');
3810
3811                 buf.Append (GetName (property.PropertyType)).Append (' ');
3812
3813                 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3814                 string name = property.Name;
3815                 foreach (IMemberReference mi in defs) {
3816                         if (mi == property) {
3817                                 name = "this";
3818                                 break;
3819                         }
3820                 }
3821                 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3822         
3823                 if (property.Parameters.Count != 0) {
3824                         AppendParameters (buf, method, property.Parameters, '[', ']');
3825                 }
3826
3827                 buf.Append (" {");
3828                 if (set_visible != null) {
3829                         if (set_visible != visibility)
3830                                 buf.Append (' ').Append (set_visible);
3831                         buf.Append (" set;");
3832                 }
3833                 if (get_visible != null) {
3834                         if (get_visible != visibility)
3835                                 buf.Append (' ').Append (get_visible);
3836                         buf.Append (" get;");
3837                 }
3838                 buf.Append (" }");
3839         
3840                 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3841         }
3842
3843         protected override string GetFieldDeclaration (FieldDefinition field)
3844         {
3845                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3846                 if (declType.IsEnum && field.Name == "value__")
3847                         return null; // This member of enums aren't documented.
3848
3849                 StringBuilder buf = new StringBuilder ();
3850                 AppendFieldVisibility (buf, field);
3851                 if (buf.Length == 0)
3852                         return null;
3853
3854                 if (declType.IsEnum)
3855                         return field.Name;
3856
3857                 if (field.IsStatic && !field.IsLiteral)
3858                         buf.Append (" static");
3859                 if (field.IsInitOnly)
3860                         buf.Append (" readonly");
3861                 if (field.IsLiteral)
3862                         buf.Append (" const");
3863
3864                 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3865                 buf.Append (field.Name);
3866                 AppendFieldValue (buf, field);
3867                 buf.Append (';');
3868
3869                 return buf.ToString ();
3870         }
3871
3872         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3873         {
3874                 if (field.IsPublic)
3875                         return buf.Append ("public");
3876                 if (field.IsFamily || field.IsFamilyOrAssembly)
3877                         return buf.Append ("protected");
3878                 return buf;
3879         }
3880
3881         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3882         {
3883                 // enums have a value__ field, which we ignore
3884                 if (((TypeDefinition ) field.DeclaringType).IsEnum || 
3885                                 field.DeclaringType.IsGenericType ())
3886                         return buf;
3887                 if (field.HasConstant && field.IsLiteral) {
3888                         object val = null;
3889                         try {
3890                                 val   = field.Constant;
3891                         } catch {
3892                                 return buf;
3893                         }
3894                         if (val == null)
3895                                 buf.Append (" = ").Append ("null");
3896                         else if (val is Enum)
3897                                 buf.Append (" = ").Append (val.ToString ());
3898                         else if (val is IFormattable) {
3899                                 string value = ((IFormattable)val).ToString();
3900                                 if (val is string)
3901                                         value = "\"" + value + "\"";
3902                                 buf.Append (" = ").Append (value);
3903                         }
3904                 }
3905                 return buf;
3906         }
3907
3908         protected override string GetEventDeclaration (EventDefinition e)
3909         {
3910                 StringBuilder buf = new StringBuilder ();
3911                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3912                         return null;
3913                 }
3914
3915                 AppendModifiers (buf, e.AddMethod);
3916
3917                 buf.Append (" event ");
3918                 buf.Append (GetName (e.EventType)).Append (' ');
3919                 buf.Append (e.Name).Append (';');
3920
3921                 return buf.ToString ();
3922         }
3923 }
3924
3925 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3926         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3927         {
3928                 return buf;
3929         }
3930 }
3931
3932 class DocTypeFullMemberFormatter : MemberFormatter {
3933         public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3934
3935         protected override char NestedTypeSeparator {
3936                 get {return '+';}
3937         }
3938 }
3939
3940 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3941         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3942         {
3943                 return buf;
3944         }
3945 }
3946
3947 class SlashDocMemberFormatter : MemberFormatter {
3948
3949         protected override char[] GenericTypeContainer {
3950                 get {return new char[]{'{', '}'};}
3951         }
3952
3953         private bool AddTypeCount = true;
3954
3955         private TypeReference genDeclType;
3956         private MethodReference genDeclMethod;
3957
3958         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3959         {
3960                 if (type is GenericParameter) {
3961                         int l = buf.Length;
3962                         if (genDeclType != null) {
3963                                 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3964                                 for (int i = 0; i < genArgs.Count; ++i) {
3965                                         if (genArgs [i].Name == type.Name) {
3966                                                 buf.Append ('`').Append (i);
3967                                                 break;
3968                                         }
3969                                 }
3970                         }
3971                         if (genDeclMethod != null) {
3972                                 GenericParameterCollection genArgs = null;
3973                                 if (genDeclMethod.IsGenericMethod ()) {
3974                                         genArgs = genDeclMethod.GenericParameters;
3975                                         for (int i = 0; i < genArgs.Count; ++i) {
3976                                                 if (genArgs [i].Name == type.Name) {
3977                                                         buf.Append ("``").Append (i);
3978                                                         break;
3979                                                 }
3980                                         }
3981                                 }
3982                         }
3983                         if (genDeclType == null && genDeclMethod == null) {
3984                                 // Probably from within an explicitly implemented interface member,
3985                                 // where CSC uses parameter names instead of indices (why?), e.g.
3986                                 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3987                                 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3988                                 buf.Append (type.Name);
3989                         }
3990                         if (buf.Length == l) {
3991                                 throw new Exception (string.Format (
3992                                                 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}", 
3993                                                 type.Name, genDeclType, genDeclMethod));
3994                         }
3995                 }
3996                 else {
3997                         base.AppendTypeName (buf, type);
3998                         if (AddTypeCount) {
3999                                 int numArgs = type.GenericParameters.Count;
4000                                 if (type.DeclaringType != null)
4001                                         numArgs -= type.GenericParameters.Count;
4002                                 if (numArgs > 0) {
4003                                         buf.Append ('`').Append (numArgs);
4004                                 }
4005                         }
4006                 }
4007                 return buf;
4008         }
4009
4010         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4011         {
4012                 if (!AddTypeCount)
4013                         base.AppendGenericType (buf, type);
4014                 else
4015                         AppendType (buf, type);
4016                 return buf;
4017         }
4018
4019         private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4020         {
4021                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4022                 bool insertNested = false;
4023                 int prevParamCount = 0;
4024                 foreach (var decl in decls) {
4025                         if (insertNested)
4026                                 buf.Append (NestedTypeSeparator);
4027                         insertNested = true;
4028                         base.AppendTypeName (buf, decl);
4029                         int argCount = DocUtils.GetGenericArgumentCount (decl);
4030                         int numArgs = argCount - prevParamCount;
4031                         prevParamCount = argCount;
4032                         if (numArgs > 0)
4033                                 buf.Append ('`').Append (numArgs);
4034                 }
4035                 return buf;
4036         }
4037
4038         public override string GetDeclaration (IMemberReference member)
4039         {
4040                 TypeReference r = member as TypeReference;
4041                 if (r != null) {
4042                         return "T:" + GetTypeName (r);
4043                 }
4044                 return base.GetDeclaration (member);
4045         }
4046
4047         protected override string GetConstructorName (MethodReference constructor)
4048         {
4049                 return GetMethodDefinitionName (constructor, "#ctor");
4050         }
4051
4052         protected override string GetMethodName (MethodReference method)
4053         {
4054                 string name = null;
4055                 MethodDefinition methodDef = method as MethodDefinition;
4056                 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4057                         name = method.Name;
4058                 else {
4059                         TypeReference iface;
4060                         MethodReference ifaceMethod;
4061                         DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4062                         AddTypeCount = false;
4063                         name = GetTypeName (iface) + "." + ifaceMethod.Name;
4064                         AddTypeCount = true;
4065                 }
4066                 return GetMethodDefinitionName (method, name);
4067         }
4068
4069         private string GetMethodDefinitionName (MethodReference method, string name)
4070         {
4071                 StringBuilder buf = new StringBuilder ();
4072                 buf.Append (GetTypeName (method.DeclaringType));
4073                 buf.Append ('.');
4074                 buf.Append (name.Replace (".", "#"));
4075                 if (method.IsGenericMethod ()) {
4076                         GenericParameterCollection genArgs = method.GenericParameters;
4077                         if (genArgs.Count > 0)
4078                                 buf.Append ("``").Append (genArgs.Count);
4079                 }
4080                 ParameterDefinitionCollection parameters = method.Parameters;
4081                 try {
4082                         genDeclType   = method.DeclaringType;
4083                         genDeclMethod = method;
4084                         AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4085                 }
4086                 finally {
4087                         genDeclType   = null;
4088                         genDeclMethod = null;
4089                 }
4090                 return buf.ToString ();
4091         }
4092
4093         private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4094         {
4095                 if (parameters.Count == 0)
4096                         return buf;
4097
4098                 buf.Append ('(');
4099
4100                 AppendParameter (buf, genArgs, parameters [0]);
4101                 for (int i = 1; i < parameters.Count; ++i) {
4102                         buf.Append (',');
4103                         AppendParameter (buf, genArgs, parameters [i]);
4104                 }
4105
4106                 return buf.Append (')');
4107         }
4108
4109         private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4110         {
4111                 AddTypeCount = false;
4112                 buf.Append (GetTypeName (parameter.ParameterType));
4113                 AddTypeCount = true;
4114                 return buf;
4115         }
4116
4117         protected override string GetPropertyName (PropertyReference property)
4118         {
4119                 string name = null;
4120
4121                 PropertyDefinition propertyDef = property as PropertyDefinition;
4122                 MethodDefinition method = null;
4123                 if (propertyDef != null)
4124                         method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4125                 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4126                         name = property.Name;
4127                 else {
4128                         TypeReference iface;
4129                         MethodReference ifaceMethod;
4130                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4131                         AddTypeCount = false;
4132                         name = string.Join ("#", new string[]{
4133                                         GetTypeName (iface).Replace (".", "#"),
4134                                         DocUtils.GetMember (property.Name)
4135                         });
4136                         AddTypeCount = true;
4137                 }
4138
4139                 StringBuilder buf = new StringBuilder ();
4140                 buf.Append (GetName (property.DeclaringType));
4141                 buf.Append ('.');
4142                 buf.Append (name);
4143                 ParameterDefinitionCollection parameters = property.Parameters;
4144                 if (parameters.Count > 0) {
4145                         genDeclType = property.DeclaringType;
4146                         buf.Append ('(');
4147                         GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4148                         AppendParameter (buf, genArgs, parameters [0]);
4149                         for (int i = 1; i < parameters.Count; ++i) {
4150                                  buf.Append (',');
4151                                  AppendParameter (buf, genArgs, parameters [i]);
4152                         }
4153                         buf.Append (')');
4154                         genDeclType = null;
4155                 }
4156                 return buf.ToString ();
4157         }
4158
4159         protected override string GetFieldName (FieldReference field)
4160         {
4161                 return string.Format ("{0}.{1}",
4162                         GetName (field.DeclaringType), field.Name);
4163         }
4164
4165         protected override string GetEventName (EventReference e)
4166         {
4167                 return string.Format ("{0}.{1}",
4168                         GetName (e.DeclaringType), e.Name);
4169         }
4170
4171         protected override string GetTypeDeclaration (TypeDefinition type)
4172         {
4173                 string name = GetName (type);
4174                 if (type == null)
4175                         return null;
4176                 return "T:" + name;
4177         }
4178
4179         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4180         {
4181                 string name = GetName (constructor);
4182                 if (name == null)
4183                         return null;
4184                 return "M:" + name;
4185         }
4186
4187         protected override string GetMethodDeclaration (MethodDefinition method)
4188         {
4189                 string name = GetName (method);
4190                 if (name == null)
4191                         return null;
4192                 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4193                         genDeclType = method.DeclaringType;
4194                         genDeclMethod = method;
4195                         name += "~" + GetName (method.ReturnType.ReturnType);
4196                         genDeclType = null;
4197                         genDeclMethod = null;
4198                 }
4199                 return "M:" + name;
4200         }
4201
4202         protected override string GetPropertyDeclaration (PropertyDefinition property)
4203         {
4204                 string name = GetName (property);
4205                 if (name == null)
4206                         return null;
4207                 return "P:" + name;
4208         }
4209
4210         protected override string GetFieldDeclaration (FieldDefinition field)
4211         {
4212                 string name = GetName (field);
4213                 if (name == null)
4214                         return null;
4215                 return "F:" + name;
4216         }
4217
4218         protected override string GetEventDeclaration (EventDefinition e)
4219         {
4220                 string name = GetName (e);
4221                 if (name == null)
4222                         return null;
4223                 return "E:" + name;
4224         }
4225 }
4226
4227 class FileNameMemberFormatter : SlashDocMemberFormatter {
4228         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4229         {
4230                 return buf;
4231         }
4232
4233         protected override char NestedTypeSeparator {
4234                 get {return '+';}
4235         }
4236 }
4237
4238 }