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