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