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